Celestia

Форк
0
/
3dsread.cpp 
818 строк · 24.7 Кб
1
// 3dsread.cpp
2
//
3
// Copyright (C) 2000, Chris Laurel <claurel@shatters.net>
4
//
5
// This program is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU General Public License
7
// as published by the Free Software Foundation; either version 2
8
// of the License, or (at your option) any later version.
9

10
#include "3dsread.h"
11

12
#include <algorithm>
13
#include <array>
14
#include <cstddef>
15
#include <cstdint>
16
#include <cstring>
17
#include <fstream>
18
#include <istream>
19
#include <string>
20
#include <utility>
21

22
#include <Eigen/Core>
23
#include <fmt/format.h>
24

25
#include <celutil/binaryread.h>
26
#include <celutil/logger.h>
27
#include "3dsmodel.h"
28

29
namespace util = celestia::util;
30
using util::GetLogger;
31

32
namespace
33
{
34
enum class M3DChunkType : std::uint16_t
35
{
36
    Null                 = 0x0000,
37
    Version              = 0x0002,
38
    ColorFloat           = 0x0010,
39
    Color24              = 0x0011,
40
    LinColorF            = 0x0013,
41
    IntPercentage        = 0x0030,
42
    FloatPercentage      = 0x0031,
43
    MasterScale          = 0x0100,
44

45
    BackgroundColor      = 0x1200,
46

47
    Meshdata             = 0x3d3d,
48
    MeshVersion          = 0x3d3e,
49

50
    NamedObject          = 0x4000,
51
    TriangleMesh         = 0x4100,
52
    PointArray           = 0x4110,
53
    PointFlagArray       = 0x4111,
54
    FaceArray            = 0x4120,
55
    MeshMaterialGroup    = 0x4130,
56
    MeshTextureCoords    = 0x4140,
57
    MeshSmoothGroup      = 0x4150,
58
    MeshMatrix           = 0x4160,
59
    Magic                = 0x4d4d,
60

61
    MaterialName         = 0xa000,
62
    MaterialAmbient      = 0xa010,
63
    MaterialDiffuse      = 0xa020,
64
    MaterialSpecular     = 0xa030,
65
    MaterialShininess    = 0xa040,
66
    MaterialShin2Pct     = 0xa041,
67
    MaterialTransparency = 0xa050,
68
    MaterialXpfall       = 0xa052,
69
    MaterialRefblur      = 0xa053,
70
    MaterialSelfIllum    = 0xa084,
71
    MaterialWiresize     = 0xa087,
72
    MaterialXpfallin     = 0xa08a,
73
    MaterialShading      = 0xa100,
74
    MaterialTexmap       = 0xa200,
75
    MaterialMapname      = 0xa300,
76
    MaterialEntry        = 0xafff,
77

78
    Kfdata               = 0xb000,
79
};
80

81

82
constexpr auto chunkHeaderSize = static_cast<std::int32_t>(sizeof(M3DChunkType) + sizeof(std::int32_t));
83

84
} // end unnamed namespace
85

86

87
template<>
88
struct fmt::formatter<M3DChunkType>
89
{
90
    constexpr auto parse(const format_parse_context& ctx) const -> decltype(ctx.begin()) {
91
        // we should validate the format here but exceptions are disabled
92
        return ctx.begin();
93
    }
94

95
    template<typename FormatContext>
96
    auto format(const M3DChunkType& chunkType, FormatContext& ctx) -> decltype(ctx.out()) {
97
        return format_to(ctx.out(), "{:04x}", static_cast<std::uint16_t>(chunkType));
98
    }
99
};
100

101

102
namespace
103
{
104

105
bool readChunkType(std::istream& in, M3DChunkType& chunkType)
106
{
107
    std::uint16_t value;
108
    if (!util::readLE<std::uint16_t>(in, value)) { return false; }
109
    chunkType = static_cast<M3DChunkType>(value);
110
    return true;
111
}
112

113

114
bool readString(std::istream& in, std::int32_t& contentSize, std::string& value)
115
{
116
    if (contentSize == 0)
117
    {
118
        value.clear();
119
        return true;
120
    }
121

122
    std::size_t max_length = std::min(64, contentSize);
123
    value.resize(max_length);
124
    in.getline(value.data(), max_length, '\0');
125
    if (!in.good())
126
    {
127
        GetLogger()->error("Error occurred reading string\n");
128
        return false;
129
    }
130

131
    value.resize(in.gcount() - 1);
132
    contentSize -= in.gcount();
133
    return true;
134
}
135

136

137
bool skipChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize)
138
{
139
    GetLogger()->debug("Skipping {} bytes of unknown/unexpected chunk type {}\n", contentSize, chunkType);
140
    in.ignore(contentSize);
141
    if (in.good() || in.eof()) { return true; }
142

143
    GetLogger()->error("Error skipping {} bytes of unknown/unexpected chunk type {}\n", contentSize, chunkType);
144
    return false;
145
}
146

147

148
bool skipTrailing(std::istream& in, std::int32_t contentSize)
149
{
150
    if (contentSize < 0)
151
    {
152
        GetLogger()->error("Negative trailing chunk size {} detected", contentSize);
153
        return false;
154
    }
155

156
    if (contentSize == 0) { return true; }
157

158
    GetLogger()->debug("Skipping {} trailing bytes\n", contentSize);
159
    in.ignore(contentSize);
160
    if (in.good() || in.eof()) { return true; }
161

162
    GetLogger()->error("Error skipping {} trailing bytes\n", contentSize);
163
    return false;
164
}
165

166

167
template<typename T, typename ProcessFunc>
168
bool readChunks(std::istream& in, std::int32_t contentSize, T& obj, ProcessFunc processChunk)
169
{
170
    while (contentSize > chunkHeaderSize)
171
    {
172
        if (in.eof())
173
        {
174
            GetLogger()->warn("Unexpected EOF detected, stopping processing\n");
175
            return true;
176
        }
177

178
        M3DChunkType chunkType;
179
        if (!readChunkType(in, chunkType))
180
        {
181
            GetLogger()->error("Failed to read chunk type\n");
182
            return false;
183
        }
184

185
        GetLogger()->debug("Found chunk type {}\n", chunkType);
186

187
        std::int32_t chunkSize;
188
        if (!util::readLE<std::int32_t>(in, chunkSize))
189
        {
190
            GetLogger()->error("Failed to read chunk size\n", chunkType);
191
            return false;
192
        }
193
        else if (chunkSize < chunkHeaderSize)
194
        {
195
            GetLogger()->error("Chunk size {} too small to include header\n", chunkSize);
196
            return false;
197
        }
198
        else if (chunkSize > contentSize)
199
        {
200
            GetLogger()->error("Chunk size {} exceeds remaining content size {} of outer chunk\n", chunkSize, contentSize);
201
            return false;
202
        }
203

204
        if (!processChunk(in, chunkType, chunkSize - chunkHeaderSize, obj))
205
        {
206
            GetLogger()->debug("Failed to process inner chunk\n");
207
            return false;
208
        }
209

210
        contentSize -= chunkSize;
211
    }
212

213
    return skipTrailing(in, contentSize);
214
}
215

216

217
bool readPointArray(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
218
{
219
    constexpr auto headerSize = static_cast<std::int32_t>(sizeof(std::uint16_t));
220
    if (contentSize < headerSize)
221
    {
222
        GetLogger()->error("Content size {} too small to include point array count\n", contentSize);
223
        return false;
224
    }
225

226
    std::uint16_t nPoints;
227
    if (!util::readLE<std::uint16_t>(in, nPoints))
228
    {
229
        GetLogger()->error("Failed to read point array count\n");
230
        return false;
231
    }
232

233
    auto pointsCount = static_cast<std::int32_t>(nPoints);
234
    std::int32_t expectedSize = headerSize + pointsCount * static_cast<std::int32_t>(3 * sizeof(float));
235
    if (contentSize < expectedSize)
236
    {
237
        GetLogger()->error("Content size {} too small to include point array with {} entries", contentSize);
238
        return false;
239
    }
240

241
    for (std::int32_t i = 0; i < pointsCount; ++i)
242
    {
243
        Eigen::Vector3f vertex;
244
        if (!util::readLE<float>(in, vertex.x())
245
            || !util::readLE<float>(in, vertex.y())
246
            || !util::readLE<float>(in, vertex.z()))
247
        {
248
            GetLogger()->error("Failed to read entry {} of point array\n", i);
249
            return false;
250
        }
251

252
        triMesh.addVertex(vertex);
253
    }
254

255
    return skipTrailing(in, contentSize - expectedSize);
256
}
257

258

259
bool readTextureCoordArray(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
260
{
261
    constexpr auto headerSize = static_cast<std::int32_t>(sizeof(std::uint16_t));
262
    if (contentSize < headerSize)
263
    {
264
        GetLogger()->error("Content size {} too small to include texture coord array count\n", contentSize);
265
        return false;
266
    }
267

268
    std::uint16_t nTexCoords;
269
    if (!util::readLE<std::uint16_t>(in, nTexCoords))
270
    {
271
        GetLogger()->error("Failed to read texture coord array count\n");
272
        return false;
273
    }
274

275
    auto texCoordsCount = static_cast<std::int32_t>(nTexCoords);
276
    std::int32_t expectedSize = headerSize + texCoordsCount * static_cast<std::int32_t>(2 * sizeof(float));
277
    if (contentSize < expectedSize)
278
    {
279
        GetLogger()->error("Content size {} too small to include texture coord array with {} entries\n", contentSize, nTexCoords);
280
        return false;
281
    }
282

283
    for (std::int32_t i = 0; i < texCoordsCount; ++i)
284
    {
285
        Eigen::Vector2f texCoord;
286
        if (!util::readLE<float>(in, texCoord.x())
287
            || !util::readLE<float>(in, texCoord.y()))
288
        {
289
            GetLogger()->error("Failed to read entry {} of texture coord array\n", i);
290
            return false;
291
        }
292

293
        texCoord.y() = -texCoord.y();
294
        triMesh.addTexCoord(texCoord);
295
    }
296

297
    return skipTrailing(in, contentSize - expectedSize);
298
}
299

300

301
bool readMeshMaterialGroup(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
302
{
303
    M3DMeshMaterialGroup matGroup;
304
    if (!readString(in, contentSize, matGroup.materialName)) { return false; }
305
    constexpr auto headerSize = static_cast<std::int32_t>(sizeof(std::uint16_t));
306
    if (contentSize < headerSize)
307
    {
308
        GetLogger()->error("Remaining content size {} too small to include material group face array count\n", contentSize);
309
        return false;
310
    }
311

312
    std::uint16_t nFaces;
313
    if (!util::readLE<std::uint16_t>(in, nFaces))
314
    {
315
        GetLogger()->error("Failed to read material group face array count\n");
316
        return false;
317
    }
318

319
    auto faceCount = static_cast<std::int32_t>(nFaces);
320
    std::int32_t expectedSize = headerSize + faceCount * static_cast<std::int32_t>(sizeof(std::uint16_t));
321
    if (contentSize < expectedSize)
322
    {
323
        GetLogger()->error("Remaining content size {} too small to include material group face array with {} entries\n", contentSize, nFaces);
324
        return false;
325
    }
326

327
    for (std::int32_t i = 0; i < faceCount; ++i)
328
    {
329
        std::uint16_t faceIndex;
330
        if (!util::readLE(in, faceIndex))
331
        {
332
            GetLogger()->error("Failed to read entry {} of material group face array\n", i);
333
            return false;
334
        }
335

336
        matGroup.faces.push_back(faceIndex);
337
    }
338

339
    triMesh.addMeshMaterialGroup(std::move(matGroup));
340
    return skipTrailing(in, contentSize - expectedSize);
341
}
342

343

344
bool readMeshSmoothGroup(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
345
{
346
    auto faceCount = static_cast<std::int32_t>(triMesh.getFaceCount());
347
    std::int32_t expectedSize = faceCount * static_cast<std::int32_t>(sizeof(std::int32_t));
348
    if (contentSize < expectedSize)
349
    {
350
        GetLogger()->error("Content size {} too small to include smoothing group array with {} entries\n", contentSize, faceCount);
351
        return false;
352
    }
353

354
    for (std::int32_t i = 0; i < faceCount; ++i)
355
    {
356
        std::uint32_t groups;
357
        if (!util::readLE<std::uint32_t>(in, groups))
358
        {
359
            GetLogger()->error("Failed to read entry {} of smoothing group array\n", i);
360
            return false;
361
        }
362

363
        triMesh.addSmoothingGroups(groups);
364
    }
365

366
    return skipTrailing(in, contentSize - expectedSize);
367
}
368

369

370
bool processFaceArrayChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DTriangleMesh& triMesh)
371
{
372
    switch (chunkType)
373
    {
374
    case M3DChunkType::MeshMaterialGroup:
375
        GetLogger()->debug("Processing MeshMaterialGroup chunk\n");
376
        return readMeshMaterialGroup(in, contentSize, triMesh);
377

378
    case M3DChunkType::MeshSmoothGroup:
379
        GetLogger()->debug("Processing MeshSmoothGroup chunk\n");
380
        return readMeshSmoothGroup(in, contentSize, triMesh);
381

382
    default:
383
        return skipChunk(in, chunkType, contentSize);
384
    }
385
}
386

387

388
bool readFaceArray(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
389
{
390
    constexpr auto headerSize = static_cast<std::int32_t>(sizeof(std::uint16_t));
391
    if (contentSize < headerSize)
392
    {
393
        GetLogger()->error("Content size {} too small to include face array count\n", contentSize);
394
        return false;
395
    }
396

397
    std::uint16_t nFaces;
398
    if (!util::readLE<std::uint16_t>(in, nFaces))
399
    {
400
        GetLogger()->error("Failed to read face array count\n");
401
        return false;
402
    }
403

404
    auto faceCount = static_cast<std::int32_t>(nFaces);
405
    std::int32_t expectedSize = headerSize + faceCount * static_cast<std::int32_t>(4 * sizeof(std::uint16_t));
406
    if (contentSize < expectedSize)
407
    {
408
        GetLogger()->error("Content size {} too small to include face array with {} entries\n", contentSize, nFaces);
409
        return false;
410
    }
411

412
    for (std::int32_t i = 0; i < faceCount; ++i)
413
    {
414
        std::uint16_t v0, v1, v2, flags;
415
        if (!util::readLE<std::uint16_t>(in, v0)
416
            || !util::readLE<std::uint16_t>(in, v1)
417
            || !util::readLE<std::uint16_t>(in, v2)
418
            || !util::readLE<std::uint16_t>(in, flags))
419
        {
420
            GetLogger()->error("Failed to read entry {} of face array\n", i);
421
            return false;
422
        }
423

424
        triMesh.addFace(v0, v1, v2);
425
    }
426

427
    if (expectedSize < contentSize)
428
    {
429
        return readChunks(in, contentSize - expectedSize, triMesh, processFaceArrayChunk);
430
    }
431

432
    return true;
433
}
434

435

436
bool readMeshMatrix(std::istream& in, std::int32_t contentSize, M3DTriangleMesh& triMesh)
437
{
438
    constexpr auto expectedSize = static_cast<std::int32_t>(sizeof(float) * 12);
439
    if (contentSize < expectedSize)
440
    {
441
        GetLogger()->error("Content size {} too small to include mesh matrix\n", contentSize);
442
        return false;
443
    }
444

445
    std::array<float, 12> elements;
446
    for (std::size_t i = 0; i < 12; ++i)
447
    {
448
        if (!util::readLE<float>(in, elements[i]))
449
        {
450
            GetLogger()->error("Failed to read element {} of mesh matrix\n", i);
451
            return false;
452
        }
453
    }
454

455
    Eigen::Matrix4f matrix;
456
    matrix << elements[0], elements[1], elements[2], 0,
457
                elements[3], elements[4], elements[5], 0,
458
                elements[6], elements[7], elements[8], 0,
459
                elements[9], elements[10], elements[11], 1;
460
    triMesh.setMatrix(matrix);
461

462
    return skipTrailing(in, contentSize - expectedSize);
463
}
464

465

466
bool processTriangleMeshChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DTriangleMesh& triMesh)
467
{
468
    switch (chunkType)
469
    {
470
    case M3DChunkType::PointArray:
471
        GetLogger()->debug("Processing PointArray chunk\n");
472
        return readPointArray(in, contentSize, triMesh);
473

474
    case M3DChunkType::MeshTextureCoords:
475
        GetLogger()->debug("Processing MeshTextureCoords chunk\n");
476
        return readTextureCoordArray(in, contentSize, triMesh);
477

478
    case M3DChunkType::FaceArray:
479
        GetLogger()->debug("Processing FaceArray chunk\n");
480
        return readFaceArray(in, contentSize, triMesh);
481

482
    case M3DChunkType::MeshMatrix:
483
        GetLogger()->debug("Processing MeshMatrix chunk\n");
484
        return readMeshMatrix(in, contentSize, triMesh);
485

486
    default:
487
        return skipChunk(in, chunkType, contentSize);
488
    }
489
}
490

491

492
bool processModelChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DModel& model)
493
{
494
    if (chunkType != M3DChunkType::TriangleMesh)
495
    {
496
        return skipChunk(in, chunkType, contentSize);
497
    }
498

499
    GetLogger()->debug("Processing TriangleMesh chunk\n");
500
    M3DTriangleMesh triMesh;
501
    if (!readChunks(in, contentSize, triMesh, processTriangleMeshChunk)) { return false; }
502
    model.addTriMesh(std::move(triMesh));
503
    return true;
504
}
505

506

507
bool readColor24(std::istream& in, std::int32_t contentSize, M3DColor& color)
508
{
509
    if (contentSize < 3)
510
    {
511
        GetLogger()->warn("Content size {} too small to include 24-bit color\n", contentSize);
512
        return skipTrailing(in, contentSize);
513
    }
514

515
    std::array<char, 3> rgb;
516
    if (!in.read(rgb.data(), rgb.size()).good())
517
    {
518
        GetLogger()->error("Error reading Color24 RGB values");
519
        return false;
520
    }
521

522
    color.red = static_cast<float>(static_cast<std::uint8_t>(rgb[0])) / 255.0f;
523
    color.green = static_cast<float>(static_cast<std::uint8_t>(rgb[1])) / 255.0f;
524
    color.blue = static_cast<float>(static_cast<std::uint8_t>(rgb[2])) / 255.0f;
525

526
    return skipTrailing(in, contentSize - 3);
527
}
528

529

530
bool readColorFloat(std::istream& in, std::int32_t contentSize, M3DColor& color)
531
{
532
    constexpr auto expectedSize = static_cast<std::int32_t>(sizeof(float) * 3);
533
    GetLogger()->debug("Processing ColorFloat chunk\n");
534
    if (contentSize < expectedSize)
535
    {
536
        GetLogger()->warn("Content size {} too small to include float color\n", contentSize);
537
        return skipTrailing(in, contentSize);
538
    }
539

540
    if (!util::readLE<float>(in, color.red)
541
        || !util::readLE<float>(in, color.green)
542
        || !util::readLE<float>(in, color.blue))
543
    {
544
        GetLogger()->error("Error reading ColorFloat RGB values");
545
        return false;
546
    }
547

548
    return skipTrailing(in, contentSize - expectedSize);
549
}
550

551

552
bool processColorChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DColor& color)
553
{
554
    switch (chunkType)
555
    {
556
    case M3DChunkType::Color24:
557
        GetLogger()->debug("Processing Color24 chunk\n");
558
        return readColor24(in, contentSize, color);
559

560
    case M3DChunkType::ColorFloat:
561
        GetLogger()->debug("Processing ColorFloat chunk\n");
562
        return readColorFloat(in, contentSize, color);
563

564
    default:
565
        GetLogger()->warn("Unknown color chunk type {}\n", chunkType);
566
        return skipChunk(in, chunkType, contentSize);
567
    }
568
}
569

570

571
bool readIntPercentage(std::istream& in, std::int32_t contentSize, float& percentage)
572
{
573
    constexpr auto expectedSize = static_cast<std::int32_t>(sizeof(std::int16_t));
574
    if (contentSize < expectedSize)
575
    {
576
        GetLogger()->warn("Content size {} too small to include integer perecentage\n", contentSize);
577
        return skipTrailing(in, contentSize);
578
    }
579

580
    std::int16_t value;
581
    if (!util::readLE<std::int16_t>(in, value))
582
    {
583
        GetLogger()->error("Error reading IntPercentage\n");
584
        return false;
585
    }
586

587
    percentage = static_cast<float>(value);
588
    return skipTrailing(in, contentSize - expectedSize);
589
}
590

591

592
bool readFloatPercentage(std::istream& in, std::int32_t contentSize, float& percentage)
593
{
594
    constexpr auto expectedSize = static_cast<std::int32_t>(sizeof(float));
595
    if (contentSize < expectedSize)
596
    {
597
        GetLogger()->warn("Content size {} too small to include float percentage\n", contentSize);
598
        return skipTrailing(in, contentSize);
599
    }
600

601
    if (!util::readLE<float>(in, percentage))
602
    {
603
        GetLogger()->error("Error reading FloatPercentage\n");
604
        return false;
605
    }
606

607
    return skipTrailing(in, contentSize - expectedSize);
608
}
609

610

611
bool processPercentageChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, float& percentage)
612
{
613
    switch (chunkType)
614
    {
615
    case M3DChunkType::IntPercentage:
616
        GetLogger()->debug("Processing IntPercentage chunk\n");
617
        return readIntPercentage(in, contentSize, percentage);
618

619
    case M3DChunkType::FloatPercentage:
620
        GetLogger()->debug("Processing FloatPercentage chunk\n");
621
        readFloatPercentage(in, contentSize, percentage);
622

623
    default:
624
        GetLogger()->warn("Unknown percentage {}\n", chunkType);
625
        return skipChunk(in, chunkType, contentSize);
626
    }
627
}
628

629

630
bool processTexmapChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DMaterial& material)
631
{
632
    if (chunkType != M3DChunkType::MaterialMapname)
633
    {
634
        return skipChunk(in, chunkType, contentSize);
635
    }
636

637
    GetLogger()->debug("Processing MaterialMapname chunk\n");
638
    std::string name;
639
    if (!readString(in, contentSize, name)) { return false; }
640
    material.setTextureMap(std::move(name));
641
    return skipTrailing(in, contentSize);
642
}
643

644

645
bool readMaterialName(std::istream& in, std::int32_t contentSize, M3DMaterial& material)
646
{
647
    std::string name;
648
    if (!readString(in, contentSize, name)) { return false; }
649
    material.setName(std::move(name));
650
    return skipTrailing(in, contentSize);
651
}
652

653

654
template<typename Setter>
655
bool readMaterialColor(std::istream& in, std::int32_t contentSize, Setter setter)
656
{
657
    M3DColor color;
658
    if (!readChunks(in, contentSize, color, processColorChunk)) { return false; }
659
    setter(color);
660
    return true;
661
}
662

663

664
template<typename Setter>
665
bool readMaterialPercentage(std::istream& in, std::int32_t contentSize, Setter setter)
666
{
667
    float percentage = 0.0f;
668
    if (!readChunks(in, contentSize, percentage, processPercentageChunk)) { return false; }
669
    setter(percentage);
670
    return true;
671
}
672

673

674
bool processMaterialChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DMaterial& material)
675
{
676
    switch (chunkType)
677
    {
678
    case M3DChunkType::MaterialName:
679
        GetLogger()->debug("Processing MaterialName chunk\n");
680
        return readMaterialName(in, contentSize, material);
681

682
    case M3DChunkType::MaterialAmbient:
683
        GetLogger()->debug("Processing MaterialAmbient chunk\n");
684
        return readMaterialColor(in, contentSize, [&material](M3DColor color) { material.setAmbientColor(color); });
685

686
    case M3DChunkType::MaterialDiffuse:
687
        GetLogger()->debug("Processing MaterialDiffuse chunk\n");
688
        return readMaterialColor(in, contentSize, [&material](M3DColor color) { material.setDiffuseColor(color); });
689

690
    case M3DChunkType::MaterialSpecular:
691
        GetLogger()->debug("Processing MaterialSpecular chunk\n");
692
        return readMaterialColor(in, contentSize, [&material](M3DColor color) { material.setSpecularColor(color); });
693

694
    case M3DChunkType::MaterialShininess:
695
        GetLogger()->debug("Processing MaterialShininess chunk\n");
696
        return readMaterialPercentage(in, contentSize, [&material](float percentage) { material.setShininess(percentage); });
697

698
    case M3DChunkType::MaterialTransparency:
699
        GetLogger()->debug("Processing MaterialTransparency chunk\n");
700
        return readMaterialPercentage(in, contentSize,
701
                                      [&material](float percentage) { material.setOpacity(1.0f - percentage / 100.0f); });
702

703
    case M3DChunkType::MaterialTexmap:
704
        GetLogger()->debug("Processing MaterialTexmap chunk\n");
705
        return readChunks(in, contentSize, material, processTexmapChunk);
706

707
    default:
708
        return skipChunk(in, chunkType, contentSize);
709
    }
710
}
711

712

713
bool readNamedObject(std::istream& in, std::int32_t contentSize, M3DScene& scene)
714
{
715
    std::string name;
716
    if (!readString(in, contentSize, name)) { return false; }
717
    M3DModel model;
718
    model.setName(std::move(name));
719
    if (!readChunks(in, contentSize, model, processModelChunk)) { return false; }
720
    scene.addModel(std::move(model));
721
    return true;
722
}
723

724

725
bool readMaterialEntry(std::istream& in, std::int32_t contentSize, M3DScene& scene)
726
{
727
    M3DMaterial material;
728
    if (!readChunks(in, contentSize, material, processMaterialChunk)) { return false; }
729
    scene.addMaterial(std::move(material));
730
    return true;
731
}
732

733

734
bool readBackgroundColor(std::istream& in, std::int32_t contentSize, M3DScene& scene)
735
{
736
    M3DColor color;
737
    if (!readChunks(in, contentSize, color, processColorChunk)) { return false; }
738
    scene.setBackgroundColor(color);
739
    return true;
740
}
741

742

743
bool processMeshdataChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DScene& scene)
744
{
745
    switch (chunkType)
746
    {
747
    case M3DChunkType::NamedObject:
748
        GetLogger()->debug("Processing NamedObject chunk\n");
749
        return readNamedObject(in, contentSize, scene);
750

751
    case M3DChunkType::MaterialEntry:
752
        GetLogger()->debug("Processing MaterialEntry chunk\n");
753
        return readMaterialEntry(in, contentSize, scene);
754

755
    case M3DChunkType::BackgroundColor:
756
        GetLogger()->debug("Processing BackgroundColor chunk\n");
757
        return readBackgroundColor(in, contentSize, scene);
758

759
    default:
760
        return skipChunk(in, chunkType, contentSize);
761
    }
762
}
763

764

765
bool processTopLevelChunk(std::istream& in, M3DChunkType chunkType, std::int32_t contentSize, M3DScene& scene)
766
{
767
    if (chunkType != M3DChunkType::Meshdata)
768
    {
769
        return skipChunk(in, chunkType, contentSize);
770
    }
771

772
    GetLogger()->debug("Processing Meshdata chunk\n");
773
    return readChunks(in, contentSize, scene, processMeshdataChunk);
774
}
775

776
} // end unnamed namespace
777

778

779
std::unique_ptr<M3DScene> Read3DSFile(std::istream& in)
780
{
781
    if (M3DChunkType chunkType; !readChunkType(in, chunkType) || chunkType != M3DChunkType::Magic)
782
    {
783
        GetLogger()->error("Read3DSFile: Wrong magic number in header\n");
784
        return nullptr;
785
    }
786

787
    std::int32_t chunkSize;
788
    if (!util::readLE<std::int32_t>(in, chunkSize) || chunkSize < chunkHeaderSize)
789
    {
790
        GetLogger()->error("Read3DSFile: Error reading 3DS file top level chunk size\n");
791
        return nullptr;
792
    }
793

794
    GetLogger()->verbose("3DS file, {} bytes\n", chunkSize + chunkHeaderSize);
795

796
    auto scene = std::make_unique<M3DScene>();
797
    if (!readChunks(in, chunkSize - chunkHeaderSize, *scene, processTopLevelChunk))
798
    {
799
        return nullptr;
800
    }
801

802
    return scene;
803
}
804

805

806
std::unique_ptr<M3DScene> Read3DSFile(const fs::path& filename)
807
{
808
    std::ifstream in(filename, std::ios::in | std::ios::binary);
809
    if (!in.good())
810
    {
811
        GetLogger()->error("Read3DSFile: Error opening {}\n", filename);
812
        return nullptr;
813
    }
814

815
    std::unique_ptr<M3DScene> scene = Read3DSFile(in);
816
    in.close();
817
    return scene;
818
}
819

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

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

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

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