Coverage Report

Created: 2026-04-29 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/COB/COBLoader.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
/** @file  COBLoader.cpp
43
 *  @brief Implementation of the TrueSpace COB/SCN importer class.
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
47
48
#include "COBLoader.h"
49
#include "COBScene.h"
50
#include "PostProcessing/ConvertToLHProcess.h"
51
52
#include <assimp/LineSplitter.h>
53
#include <assimp/ParsingUtils.h>
54
#include <assimp/StreamReader.h>
55
#include <assimp/TinyFormatter.h>
56
#include <assimp/fast_atof.h>
57
#include <assimp/importerdesc.h>
58
#include <assimp/scene.h>
59
#include <assimp/DefaultLogger.hpp>
60
#include <assimp/IOSystem.hpp>
61
62
#include <memory>
63
64
namespace Assimp {
65
66
using namespace Assimp::COB;
67
using namespace Assimp::Formatter;
68
69
static constexpr float units[] = {
70
    1000.f,
71
    100.f,
72
    1.f,
73
    0.001f,
74
    1.f / 0.0254f,
75
    1.f / 0.3048f,
76
    1.f / 0.9144f,
77
    1.f / 1609.344f
78
};
79
80
static constexpr aiImporterDesc desc = {
81
    "TrueSpace Object Importer",
82
    "",
83
    "",
84
    "little-endian files only",
85
    aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
86
    0,
87
    0,
88
    0,
89
    0,
90
    "cob scn"
91
};
92
93
// ------------------------------------------------------------------------------------------------
94
// Returns whether the class can handle the format of the given file.
95
0
bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
96
0
    static const char *tokens[] = { "Caligary" };
97
0
    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
98
0
}
99
100
// ------------------------------------------------------------------------------------------------
101
// Loader meta information
102
38.0k
const aiImporterDesc *COBImporter::GetInfo() const {
103
38.0k
    return &desc;
104
38.0k
}
105
106
// ------------------------------------------------------------------------------------------------
107
// Setup configuration properties for the loader
108
0
void COBImporter::SetupProperties(const Importer * /*pImp*/) {
109
    // nothing to be done for the moment
110
0
}
111
112
// ------------------------------------------------------------------------------------------------
113
0
AI_WONT_RETURN void COBImporter::ThrowException(const std::string &msg) {
114
0
    throw DeadlyImportError("COB: ", msg);
115
0
}
116
117
// ------------------------------------------------------------------------------------------------
118
0
static bool isValidASCIIHeader(const char *head) {
119
0
    ai_assert(head != nullptr);
120
121
0
    if (strncmp(head, "Caligari ", 9) != 0) {
122
0
        COBImporter::ThrowException("Could not found magic id: `Caligari`");
123
0
    }
124
125
0
    if (strncmp(&head[9], "V00.", 4) != 0) {
126
0
        COBImporter::ThrowException("Could not found Version tag: `V00.`");
127
0
    }
128
0
    ASSIMP_LOG_INFO("File format tag: ", std::string(head + 9, 6));
129
0
    if (head[16] != 'L') {
130
0
        COBImporter::ThrowException("File is big-endian, which is not supported");
131
0
    }
132
133
0
    return true;
134
0
}
135
136
// ------------------------------------------------------------------------------------------------
137
// Imports the given file into the given scene structure.
138
0
void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
139
0
    COB::Scene scene;
140
141
0
    auto file = pIOHandler->Open(pFile, "rb");
142
0
    if (!file) {
143
0
        ThrowException("Could not open " + pFile);
144
0
    }
145
146
0
    std::unique_ptr<StreamReaderLE> stream(new StreamReaderLE(file));
147
148
    // check header
149
0
    static constexpr size_t HeaderSize = 32u;
150
0
    char head[HeaderSize] = {};
151
0
    stream->CopyAndAdvance(head, HeaderSize);
152
153
    // load data into intermediate structures
154
0
    if (head[15] == 'A') {
155
0
        if (!isValidASCIIHeader(head)) {
156
0
            ThrowException("Invalid ASCII file header");
157
0
        }
158
0
        ReadAsciiFile(scene, stream.get());
159
0
    } else {
160
0
        ReadBinaryFile(scene, stream.get());
161
0
    }
162
0
    if (scene.nodes.empty()) {
163
0
        ThrowException("No nodes loaded");
164
0
    }
165
166
    // sort faces by material indices
167
0
    for (std::shared_ptr<Node> &n : scene.nodes) {
168
0
        if (n->type == Node::TYPE_MESH) {
169
0
            Mesh &mesh = (Mesh &)(*n);
170
0
            for (Face &f : mesh.faces) {
171
0
                mesh.temp_map[f.material].push_back(&f);
172
0
            }
173
0
        }
174
0
    }
175
176
    // count meshes
177
0
    for (std::shared_ptr<Node> &n : scene.nodes) {
178
0
        if (n->type == Node::TYPE_MESH) {
179
0
            Mesh &mesh = (Mesh &)(*n);
180
0
            if (mesh.vertex_positions.size() && mesh.texture_coords.size()) {
181
0
                pScene->mNumMeshes += static_cast<unsigned int>(mesh.temp_map.size());
182
0
            }
183
0
        }
184
0
    }
185
0
    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]();
186
0
    pScene->mMaterials = new aiMaterial *[pScene->mNumMeshes]();
187
0
    pScene->mNumMeshes = 0;
188
189
    // count lights and cameras
190
0
    for (std::shared_ptr<Node> &n : scene.nodes) {
191
0
        if (n->type == Node::TYPE_LIGHT) {
192
0
            ++pScene->mNumLights;
193
0
        } else if (n->type == Node::TYPE_CAMERA) {
194
0
            ++pScene->mNumCameras;
195
0
        }
196
0
    }
197
198
0
    if (pScene->mNumLights) {
199
0
        pScene->mLights = new aiLight *[pScene->mNumLights]();
200
0
    }
201
0
    if (pScene->mNumCameras) {
202
0
        pScene->mCameras = new aiCamera *[pScene->mNumCameras]();
203
0
    }
204
0
    pScene->mNumLights = pScene->mNumCameras = 0;
205
206
    // resolve parents by their IDs and build the output graph
207
0
    std::unique_ptr<Node> root(new Group());
208
0
    for (size_t n = 0; n < scene.nodes.size(); ++n) {
209
0
        const Node &nn = *scene.nodes[n].get();
210
0
        if (nn.parent_id == 0) {
211
0
            root->temp_children.push_back(&nn);
212
0
        }
213
214
0
        for (size_t m = n; m < scene.nodes.size(); ++m) {
215
0
            const Node &mm = *scene.nodes[m].get();
216
0
            if (mm.parent_id == nn.id) {
217
0
                nn.temp_children.push_back(&mm);
218
0
            }
219
0
        }
220
0
    }
221
222
0
    pScene->mRootNode = BuildNodes(*root, scene, pScene);
223
    //flip normals after import
224
0
    FlipWindingOrderProcess flip;
225
0
    flip.Execute(pScene);
226
0
}
227
228
// ------------------------------------------------------------------------------------------------
229
0
void ConvertTexture(const std::shared_ptr<Texture> &tex, aiMaterial *out, aiTextureType type) {
230
0
    const aiString path(tex->path);
231
0
    out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0));
232
0
    out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0));
233
0
}
234
235
// ------------------------------------------------------------------------------------------------
236
0
aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fill) {
237
0
    aiNode *nd = new aiNode();
238
0
    nd->mName.Set(root.name);
239
0
    nd->mTransformation = root.transform;
240
241
    // Note to everybody believing Voodoo is appropriate here:
242
    // I know polymorphism, run as fast as you can ;-)
243
0
    if (Node::TYPE_MESH == root.type) {
244
0
        const Mesh &ndmesh = (const Mesh &)(root);
245
0
        if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
246
247
0
            using Entry = std::pair<const unsigned int, Mesh::FaceRefList>;
248
0
            for (const Entry &reflist : ndmesh.temp_map) {
249
0
                { // create mesh
250
0
                    size_t n = 0;
251
0
                    for (Face *f : reflist.second) {
252
0
                        n += f->indices.size();
253
0
                    }
254
0
                    if (!n) {
255
0
                        continue;
256
0
                    }
257
0
                    aiMesh *outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh();
258
0
                    ++nd->mNumMeshes;
259
260
0
                    outmesh->mVertices = new aiVector3D[n];
261
0
                    outmesh->mTextureCoords[0] = new aiVector3D[n];
262
263
0
                    outmesh->mFaces = new aiFace[reflist.second.size()]();
264
0
                    for (Face *f : reflist.second) {
265
0
                        if (f->indices.empty()) {
266
0
                            continue;
267
0
                        }
268
269
0
                        aiFace &fout = outmesh->mFaces[outmesh->mNumFaces++];
270
0
                        fout.mIndices = new unsigned int[f->indices.size()];
271
272
0
                        for (VertexIndex &v : f->indices) {
273
0
                            if (v.pos_idx >= ndmesh.vertex_positions.size()) {
274
0
                                ThrowException("Position index out of range");
275
0
                            }
276
0
                            if (v.uv_idx >= ndmesh.texture_coords.size()) {
277
0
                                ThrowException("UV index out of range");
278
0
                            }
279
0
                            outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[v.pos_idx];
280
0
                            outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D(
281
0
                                    ndmesh.texture_coords[v.uv_idx].x,
282
0
                                    ndmesh.texture_coords[v.uv_idx].y,
283
0
                                    0.f);
284
285
0
                            fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++;
286
0
                        }
287
0
                    }
288
0
                    outmesh->mMaterialIndex = fill->mNumMaterials;
289
0
                }
290
0
                { // create material
291
0
                    const Material *min = nullptr;
292
0
                    for (const Material &m : scin.materials) {
293
0
                        if (m.parent_id == ndmesh.id && m.matnum == reflist.first) {
294
0
                            min = &m;
295
0
                            break;
296
0
                        }
297
0
                    }
298
0
                    std::unique_ptr<const Material> defmat;
299
0
                    if (!min) {
300
0
                        ASSIMP_LOG_VERBOSE_DEBUG("Could not resolve material index ", reflist.first, " - creating default material for this slot");
301
302
0
                        defmat.reset(min = new Material());
303
0
                    }
304
305
0
                    aiMaterial *mat = new aiMaterial();
306
0
                    fill->mMaterials[fill->mNumMaterials++] = mat;
307
308
0
                    const aiString s(format("#mat_") << fill->mNumMeshes << "_" << min->matnum);
309
0
                    mat->AddProperty(&s, AI_MATKEY_NAME);
310
311
0
                    if (int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) {
312
0
                        mat->AddProperty(&tmp, 1, AI_MATKEY_ENABLE_WIREFRAME);
313
0
                    }
314
315
0
                    {
316
0
                        int shader;
317
0
                        switch (min->shader) {
318
0
                        case Material::FLAT:
319
0
                            shader = aiShadingMode_Gouraud;
320
0
                            break;
321
322
0
                        case Material::PHONG:
323
0
                            shader = aiShadingMode_Phong;
324
0
                            break;
325
326
0
                        case Material::METAL:
327
0
                            shader = aiShadingMode_CookTorrance;
328
0
                            break;
329
330
0
                        default:
331
0
                            ASSIMP_LOG_ERROR("Unknown option.");
332
0
                            ai_assert(false); // shouldn't be here
333
0
                            break;
334
0
                        }
335
0
                        mat->AddProperty(&shader, 1, AI_MATKEY_SHADING_MODEL);
336
0
                        if (shader != aiShadingMode_Gouraud) {
337
0
                            mat->AddProperty(&min->exp, 1, AI_MATKEY_SHININESS);
338
0
                        }
339
0
                    }
340
341
0
                    mat->AddProperty(&min->ior, 1, AI_MATKEY_REFRACTI);
342
0
                    mat->AddProperty(&min->rgb, 1, AI_MATKEY_COLOR_DIFFUSE);
343
344
0
                    aiColor3D c = aiColor3D(min->rgb) * min->ks;
345
0
                    mat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR);
346
347
0
                    c = aiColor3D(min->rgb) * min->ka;
348
0
                    mat->AddProperty(&c, 1, AI_MATKEY_COLOR_AMBIENT);
349
350
                    // convert textures if some exist.
351
0
                    if (min->tex_color) {
352
0
                        ConvertTexture(min->tex_color, mat, aiTextureType_DIFFUSE);
353
0
                    }
354
0
                    if (min->tex_env) {
355
0
                        ConvertTexture(min->tex_env, mat, aiTextureType_UNKNOWN);
356
0
                    }
357
0
                    if (min->tex_bump) {
358
0
                        ConvertTexture(min->tex_bump, mat, aiTextureType_HEIGHT);
359
0
                    }
360
0
                }
361
0
            }
362
0
        }
363
0
    } else if (Node::TYPE_LIGHT == root.type) {
364
0
        const Light &ndlight = (const Light &)(root);
365
0
        aiLight *outlight = fill->mLights[fill->mNumLights++] = new aiLight();
366
367
0
        outlight->mName.Set(ndlight.name);
368
0
        outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color;
369
370
0
        outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle);
371
0
        outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle);
372
373
        // XXX
374
0
        outlight->mType = ndlight.ltype == Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL;
375
0
    } else if (Node::TYPE_CAMERA == root.type) {
376
0
        const Camera &ndcam = (const Camera &)(root);
377
0
        aiCamera *outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera();
378
379
0
        outcam->mName.Set(ndcam.name);
380
0
    }
381
382
    // add meshes
383
0
    if (nd->mNumMeshes) { // mMeshes must be nullptr if count is 0
384
0
        nd->mMeshes = new unsigned int[nd->mNumMeshes];
385
0
        for (unsigned int i = 0; i < nd->mNumMeshes; ++i) {
386
0
            nd->mMeshes[i] = fill->mNumMeshes - i - 1;
387
0
        }
388
0
    }
389
390
    // add children recursively
391
0
    if (!root.temp_children.empty()) {
392
0
        nd->mChildren = new aiNode *[root.temp_children.size()]();
393
0
        for (const Node *n : root.temp_children) {
394
0
            (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n, scin, fill))->mParent = nd;
395
0
        }
396
0
    }
397
398
0
    return nd;
399
0
}
400
401
// ------------------------------------------------------------------------------------------------
402
// Read an ASCII file into the given scene data structure
403
0
void COBImporter::ReadAsciiFile(Scene &out, StreamReaderLE *stream) {
404
0
    ChunkInfo ci;
405
0
    for (LineSplitter splitter(*stream); splitter; ++splitter) {
406
407
        // add all chunks to be recognized here. /else ../ omitted intentionally.
408
0
        if (splitter.match_start("PolH ")) {
409
0
            ReadChunkInfo_Ascii(ci, splitter);
410
0
            ReadPolH_Ascii(out, splitter, ci);
411
0
        }
412
0
        if (splitter.match_start("BitM ")) {
413
0
            ReadChunkInfo_Ascii(ci, splitter);
414
0
            ReadBitM_Ascii(out, splitter, ci);
415
0
        }
416
0
        if (splitter.match_start("Mat1 ")) {
417
0
            ReadChunkInfo_Ascii(ci, splitter);
418
0
            ReadMat1_Ascii(out, splitter, ci);
419
0
        }
420
0
        if (splitter.match_start("Grou ")) {
421
0
            ReadChunkInfo_Ascii(ci, splitter);
422
0
            ReadGrou_Ascii(out, splitter, ci);
423
0
        }
424
0
        if (splitter.match_start("Lght ")) {
425
0
            ReadChunkInfo_Ascii(ci, splitter);
426
0
            ReadLght_Ascii(out, splitter, ci);
427
0
        }
428
0
        if (splitter.match_start("Came ")) {
429
0
            ReadChunkInfo_Ascii(ci, splitter);
430
0
            ReadCame_Ascii(out, splitter, ci);
431
0
        }
432
0
        if (splitter.match_start("Bone ")) {
433
0
            ReadChunkInfo_Ascii(ci, splitter);
434
0
            ReadBone_Ascii(out, splitter, ci);
435
0
        }
436
0
        if (splitter.match_start("Chan ")) {
437
0
            ReadChunkInfo_Ascii(ci, splitter);
438
0
            ReadChan_Ascii(out, splitter, ci);
439
0
        }
440
0
        if (splitter.match_start("Unit ")) {
441
0
            ReadChunkInfo_Ascii(ci, splitter);
442
0
            ReadUnit_Ascii(out, splitter, ci);
443
0
        }
444
0
        if (splitter.match_start("END ")) {
445
            // we don't need this, but I guess there is a reason this
446
            // chunk has been implemented into COB for.
447
0
            return;
448
0
        }
449
0
    }
450
0
}
451
452
// ------------------------------------------------------------------------------------------------
453
0
void COBImporter::ReadChunkInfo_Ascii(ChunkInfo &out, const LineSplitter &splitter) {
454
0
    const char *all_tokens[8];
455
0
    splitter.get_tokens(all_tokens);
456
457
0
    out.version = (all_tokens[1][1] - '0') * 100 + (all_tokens[1][3] - '0') * 10 + (all_tokens[1][4] - '0');
458
0
    out.id = strtoul10(all_tokens[3]);
459
0
    out.parent_id = strtoul10(all_tokens[5]);
460
0
    out.size = strtol10(all_tokens[7]);
461
0
}
462
463
// ------------------------------------------------------------------------------------------------
464
0
void COBImporter::UnsupportedChunk_Ascii(LineSplitter &splitter, const ChunkInfo &nfo, const char *name) {
465
0
    const std::string error = format("Encountered unsupported chunk: ") << name << " [version: " << nfo.version << ", size: " << nfo.size << "]";
466
467
    // we can recover if the chunk size was specified.
468
0
    if (nfo.size != static_cast<unsigned int>(-1)) {
469
0
        ASSIMP_LOG_ERROR(error);
470
471
        // (HACK) - our current position in the stream is the beginning of the
472
        // head line of the next chunk. That's fine, but the caller is going
473
        // to call ++ on `splitter`, which we need to swallow to avoid
474
        // missing the next line.
475
0
        splitter.get_stream().IncPtr(nfo.size);
476
0
        splitter.swallow_next_increment();
477
0
    } else {
478
0
        ThrowException(error);
479
0
    }
480
0
}
481
482
// ------------------------------------------------------------------------------------------------
483
0
void COBImporter::ReadBasicNodeInfo_Ascii(Node &msh, LineSplitter &splitter, const ChunkInfo & /*nfo*/) {
484
0
    for (; splitter; ++splitter) {
485
0
        if (splitter.match_start("Name")) {
486
0
            msh.name = std::string(splitter[1]);
487
488
            // make nice names by merging the dupe count
489
0
            std::replace(msh.name.begin(), msh.name.end(),
490
0
                    ',', '_');
491
0
        } else if (splitter.match_start("Transform")) {
492
0
            for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
493
0
                const char *s = splitter->c_str();
494
0
                const char *end = s + splitter->size();
495
0
                for (unsigned int x = 0; x < 4; ++x) {
496
0
                    SkipSpaces(&s, end);
497
0
                    msh.transform[y][x] = fast_atof(&s);
498
0
                }
499
0
            }
500
            // we need the transform chunk, so we won't return until we have it.
501
0
            return;
502
0
        }
503
0
    }
504
0
}
505
506
// ------------------------------------------------------------------------------------------------
507
template <typename T>
508
0
void COBImporter::ReadFloat3Tuple_Ascii(T &fill, const char **in, const char *end) {
509
0
    const char *rgb = *in;
510
0
    for (unsigned int i = 0; i < 3; ++i) {
511
0
        SkipSpaces(&rgb, end);
512
0
        if (*rgb == ',') ++rgb;
513
0
        SkipSpaces(&rgb, end);
514
515
0
        fill[i] = fast_atof(&rgb);
516
0
    }
517
0
    *in = rgb;
518
0
}
519
520
// ------------------------------------------------------------------------------------------------
521
0
void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
522
0
    if (nfo.version > 8) {
523
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Mat1");
524
0
    }
525
526
0
    ++splitter;
527
0
    if (!splitter.match_start("mat# ")) {
528
0
        ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id);
529
0
        return;
530
0
    }
531
532
0
    out.materials.emplace_back();
533
0
    Material &mat = out.materials.back();
534
0
    mat = nfo;
535
536
0
    mat.matnum = strtoul10(splitter[1]);
537
0
    ++splitter;
538
539
0
    if (!splitter.match_start("shader: ")) {
540
0
        ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id);
541
0
        return;
542
0
    }
543
0
    std::string shader = std::string(splitter[1]);
544
0
    shader = shader.substr(0, shader.find_first_of(" \t"));
545
546
0
    if (shader == "metal") {
547
0
        mat.shader = Material::METAL;
548
0
    } else if (shader == "phong") {
549
0
        mat.shader = Material::PHONG;
550
0
    } else if (shader != "flat") {
551
0
        ASSIMP_LOG_WARN("Unknown value for `shader` in `Mat1` chunk ", nfo.id);
552
0
    }
553
554
0
    ++splitter;
555
0
    if (!splitter.match_start("rgb ")) {
556
0
        ASSIMP_LOG_WARN("Expected `rgb` line in `Mat1` chunk ", nfo.id);
557
0
    }
558
559
0
    const char *rgb = splitter[1];
560
0
    ReadFloat3Tuple_Ascii(mat.rgb, &rgb, splitter.getEnd());
561
562
0
    ++splitter;
563
0
    if (!splitter.match_start("alpha ")) {
564
0
        ASSIMP_LOG_WARN("Expected `alpha` line in `Mat1` chunk ", nfo.id);
565
0
    }
566
567
0
    const char *tokens[10];
568
0
    splitter.get_tokens(tokens);
569
570
0
    mat.alpha = fast_atof(tokens[1]);
571
0
    mat.ka = fast_atof(tokens[3]);
572
0
    mat.ks = fast_atof(tokens[5]);
573
0
    mat.exp = fast_atof(tokens[7]);
574
0
    mat.ior = fast_atof(tokens[9]);
575
0
}
576
577
// ------------------------------------------------------------------------------------------------
578
0
void COBImporter::ReadUnit_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
579
0
    if (nfo.version > 1) {
580
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Unit");
581
0
    }
582
0
    ++splitter;
583
0
    if (!splitter.match_start("Units ")) {
584
0
        ASSIMP_LOG_WARN("Expected `Units` line in `Unit` chunk ", nfo.id);
585
0
        return;
586
0
    }
587
588
    // parent chunks preceede their children, so we should have the
589
    // corresponding chunk already.
590
0
    for (std::shared_ptr<Node> &nd : out.nodes) {
591
0
        if (nd->id == nfo.parent_id) {
592
0
            const unsigned int t = strtoul10(splitter[1]);
593
594
0
            nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? (
595
0
                                                                             ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) :
596
0
                                                                     units[t];
597
0
            return;
598
0
        }
599
0
    }
600
0
    ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist");
601
0
}
602
603
// ------------------------------------------------------------------------------------------------
604
0
void COBImporter::ReadChan_Ascii(Scene & /*out*/, LineSplitter &splitter, const ChunkInfo &nfo) {
605
0
    if (nfo.version > 8) {
606
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Chan");
607
0
    }
608
0
}
609
610
// ------------------------------------------------------------------------------------------------
611
0
void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
612
0
    if (nfo.version > 8) {
613
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Lght");
614
0
    }
615
616
0
    out.nodes.push_back(std::shared_ptr<Light>(new Light()));
617
0
    Light &msh = (Light &)(*out.nodes.back().get());
618
0
    msh = nfo;
619
620
0
    ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
621
622
0
    if (splitter.match_start("Infinite ")) {
623
0
        msh.ltype = Light::INFINITE;
624
0
    } else if (splitter.match_start("Local ")) {
625
0
        msh.ltype = Light::LOCAL;
626
0
    } else if (splitter.match_start("Spot ")) {
627
0
        msh.ltype = Light::SPOT;
628
0
    } else {
629
0
        ASSIMP_LOG_WARN("Unknown kind of light source in `Lght` chunk ", nfo.id, " : ", *splitter);
630
0
        msh.ltype = Light::SPOT;
631
0
    }
632
633
0
    ++splitter;
634
0
    if (!splitter.match_start("color ")) {
635
0
        ASSIMP_LOG_WARN("Expected `color` line in `Lght` chunk ", nfo.id);
636
0
    }
637
638
0
    const char *rgb = splitter[1];
639
0
    const char *end = splitter.getEnd();
640
0
    ReadFloat3Tuple_Ascii(msh.color, &rgb, end);
641
642
0
    SkipSpaces(&rgb, end);
643
0
    if (strncmp(rgb, "cone angle", 10) != 0) {
644
0
        ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id);
645
0
    }
646
0
    SkipSpaces(rgb + 10, &rgb, end);
647
0
    msh.angle = fast_atof(&rgb);
648
649
0
    SkipSpaces(&rgb, end);
650
0
    if (strncmp(rgb, "inner angle", 11) != 0) {
651
0
        ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
652
0
    }
653
0
    SkipSpaces(rgb + 11, &rgb, end);
654
0
    msh.inner_angle = fast_atof(&rgb);
655
656
    // skip the rest for we can't handle this kind of physically-based lighting information.
657
0
}
658
659
// ------------------------------------------------------------------------------------------------
660
0
void COBImporter::ReadCame_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
661
0
    if (nfo.version > 2) {
662
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Came");
663
0
    }
664
665
0
    out.nodes.push_back(std::shared_ptr<Camera>(new Camera()));
666
0
    Camera &msh = (Camera &)(*out.nodes.back().get());
667
0
    msh = nfo;
668
669
0
    ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
670
671
    // skip the next line, we don't know this differentiation between a
672
    // standard camera and a panoramic camera.
673
0
    ++splitter;
674
0
}
675
676
// ------------------------------------------------------------------------------------------------
677
0
void COBImporter::ReadBone_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
678
0
    if (nfo.version > 5) {
679
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Bone");
680
0
    }
681
682
0
    out.nodes.push_back(std::shared_ptr<Bone>(new Bone()));
683
0
    Bone &msh = (Bone &)(*out.nodes.back().get());
684
0
    msh = nfo;
685
686
0
    ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
687
688
    // TODO
689
0
}
690
691
// ------------------------------------------------------------------------------------------------
692
0
void COBImporter::ReadGrou_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
693
0
    if (nfo.version > 1) {
694
0
        return UnsupportedChunk_Ascii(splitter, nfo, "Grou");
695
0
    }
696
697
0
    out.nodes.push_back(std::shared_ptr<Group>(new Group()));
698
0
    Group &msh = (Group &)(*out.nodes.back().get());
699
0
    msh = nfo;
700
701
0
    ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
702
0
}
703
704
// ------------------------------------------------------------------------------------------------
705
0
void COBImporter::ReadPolH_Ascii(Scene &out, LineSplitter &splitter, const ChunkInfo &nfo) {
706
0
    if (nfo.version > 8) {
707
0
        return UnsupportedChunk_Ascii(splitter, nfo, "PolH");
708
0
    }
709
710
0
    out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh()));
711
0
    Mesh &msh = (Mesh &)(*out.nodes.back().get());
712
0
    msh = nfo;
713
714
0
    ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
715
716
    // the chunk has a fixed order of components, but some are not interesting of us so
717
    // we're just looking for keywords in arbitrary order. The end of the chunk is
718
    // either the last `Face` or the `DrawFlags` attribute, depending on the format ver.
719
0
    for (; splitter; ++splitter) {
720
0
        if (splitter.match_start("World Vertices")) {
721
0
            const unsigned int cnt = strtoul10(splitter[2]);
722
0
            msh.vertex_positions.resize(cnt);
723
724
0
            for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
725
0
                const char *s = splitter->c_str();
726
0
                const char *end = splitter.getEnd();
727
0
                aiVector3D &v = msh.vertex_positions[cur];
728
729
0
                SkipSpaces(&s, end);
730
0
                v.x = fast_atof(&s);
731
0
                SkipSpaces(&s, end);
732
0
                v.y = fast_atof(&s);
733
0
                SkipSpaces(&s, end);
734
0
                v.z = fast_atof(&s);
735
0
            }
736
0
        } else if (splitter.match_start("Texture Vertices")) {
737
0
            const unsigned int cnt = strtoul10(splitter[2]);
738
0
            msh.texture_coords.resize(cnt);
739
740
0
            for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
741
0
                const char *s = splitter->c_str();
742
0
                const char *end = splitter.getEnd();
743
744
0
                aiVector2D &v = msh.texture_coords[cur];
745
746
0
                SkipSpaces(&s, end);
747
0
                v.x = fast_atof(&s);
748
0
                SkipSpaces(&s, end);
749
0
                v.y = fast_atof(&s);
750
0
            }
751
0
        } else if (splitter.match_start("Faces")) {
752
0
            const unsigned int cnt = strtoul10(splitter[1]);
753
0
            msh.faces.reserve(cnt);
754
755
0
            for (unsigned int cur = 0; cur < cnt && ++splitter; ++cur) {
756
0
                if (splitter.match_start("Hole")) {
757
0
                    ASSIMP_LOG_WARN("Skipping unsupported `Hole` line");
758
0
                    continue;
759
0
                }
760
761
0
                if (!splitter.match_start("Face")) {
762
0
                    ThrowException("Expected Face line");
763
0
                }
764
765
0
                msh.faces.emplace_back();
766
0
                Face &face = msh.faces.back();
767
768
0
                face.indices.resize(strtoul10(splitter[2]));
769
0
                face.flags = strtoul10(splitter[4]);
770
0
                face.material = strtoul10(splitter[6]);
771
772
0
                const char *s = (++splitter)->c_str();
773
0
                const char *end = splitter.getEnd();
774
0
                for (size_t i = 0; i < face.indices.size(); ++i) {
775
0
                    if (!SkipSpaces(&s, end)) {
776
0
                        ThrowException("Expected EOL token in Face entry");
777
0
                    }
778
0
                    if ('<' != *s++) {
779
0
                        ThrowException("Expected < token in Face entry");
780
0
                    }
781
0
                    face.indices[i].pos_idx = strtoul10(s, &s);
782
0
                    if (',' != *s++) {
783
0
                        ThrowException("Expected , token in Face entry");
784
0
                    }
785
0
                    face.indices[i].uv_idx = strtoul10(s, &s);
786
0
                    if ('>' != *s++) {
787
0
                        ThrowException("Expected < token in Face entry");
788
0
                    }
789
0
                }
790
0
            }
791
0
            if (nfo.version <= 4) {
792
0
                break;
793
0
            }
794
0
        } else if (splitter.match_start("DrawFlags")) {
795
0
            msh.draw_flags = strtoul10(splitter[1]);
796
0
            break;
797
0
        }
798
0
    }
799
0
}
800
801
// ------------------------------------------------------------------------------------------------
802
0
void COBImporter::ReadBitM_Ascii(Scene & /*out*/, LineSplitter &splitter, const ChunkInfo &nfo) {
803
0
    if (nfo.version > 1) {
804
0
        return UnsupportedChunk_Ascii(splitter, nfo, "BitM");
805
0
    }
806
807
0
    const unsigned int head = strtoul10((++splitter)[1]);
808
0
    if (head != sizeof(Bitmap::BitmapHeader)) {
809
0
        ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk");
810
0
        return;
811
0
    }
812
0
}
813
814
// ------------------------------------------------------------------------------------------------
815
0
void COBImporter::ReadString_Binary(std::string &out, StreamReaderLE &reader) {
816
0
    out.resize(reader.GetI2());
817
0
    for (char &c : out) {
818
0
        c = reader.GetI1();
819
0
    }
820
0
}
821
822
// ------------------------------------------------------------------------------------------------
823
0
void COBImporter::ReadBasicNodeInfo_Binary(Node &msh, StreamReaderLE &reader, const ChunkInfo & /*nfo*/) {
824
0
    const unsigned int dupes = reader.GetI2();
825
0
    ReadString_Binary(msh.name, reader);
826
827
0
    msh.name = format(msh.name) << '_' << dupes;
828
829
    // skip local axes for the moment
830
0
    reader.IncPtr(48);
831
832
0
    msh.transform = aiMatrix4x4();
833
0
    for (unsigned int y = 0; y < 3; ++y) {
834
0
        for (unsigned int x = 0; x < 4; ++x) {
835
0
            msh.transform[y][x] = reader.GetF4();
836
0
        }
837
0
    }
838
0
}
839
840
// ------------------------------------------------------------------------------------------------
841
0
void COBImporter::UnsupportedChunk_Binary(StreamReaderLE &reader, const ChunkInfo &nfo, const char *name) {
842
0
    const std::string error = format("Encountered unsupported chunk: ") << name << " [version: " << nfo.version << ", size: " << nfo.size << "]";
843
844
    // we can recover if the chunk size was specified.
845
0
    if (nfo.size != static_cast<unsigned int>(-1)) {
846
0
        ASSIMP_LOG_ERROR(error);
847
0
        reader.IncPtr(nfo.size);
848
0
    } else
849
0
        ThrowException(error);
850
0
}
851
852
// ------------------------------------------------------------------------------------------------
853
// tiny utility guard to aid me at staying within chunk boundaries.
854
class chunk_guard {
855
public:
856
    chunk_guard(const COB::ChunkInfo &nfo, StreamReaderLE &reader) :
857
0
            nfo(nfo), reader(reader), cur(reader.GetCurrentPos()) {
858
        // empty
859
0
    }
860
861
0
    ~chunk_guard() {
862
        // don't do anything if the size is not given
863
0
        if (nfo.size != static_cast<unsigned int>(-1)) {
864
0
            try {
865
0
                reader.IncPtr(static_cast<int>(nfo.size) - reader.GetCurrentPos() + cur);
866
0
            } catch (const DeadlyImportError &) {
867
                // out of limit so correct the value
868
0
                reader.IncPtr(reader.GetReadLimit());
869
0
            }
870
0
        }
871
0
    }
872
873
private:
874
    const COB::ChunkInfo &nfo;
875
    StreamReaderLE &reader;
876
    long cur;
877
};
878
879
// ------------------------------------------------------------------------------------------------
880
0
void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) {
881
0
    if (nullptr == reader) {
882
0
        return;
883
0
    }
884
885
0
    while (true) {
886
0
        std::string type;
887
0
        type += reader->GetI1();
888
0
        type += reader->GetI1();
889
0
        type += reader->GetI1();
890
0
        type += reader->GetI1();
891
892
0
        ChunkInfo nfo;
893
0
        nfo.version = reader->GetI2() * 10;
894
0
        nfo.version += reader->GetI2();
895
896
0
        nfo.id = reader->GetI4();
897
0
        nfo.parent_id = reader->GetI4();
898
0
        nfo.size = reader->GetI4();
899
900
0
        if (type == "PolH") {
901
0
            ReadPolH_Binary(out, *reader, nfo);
902
0
        } else if (type == "BitM") {
903
0
            ReadBitM_Binary(out, *reader, nfo);
904
0
        } else if (type == "Grou") {
905
0
            ReadGrou_Binary(out, *reader, nfo);
906
0
        } else if (type == "Lght") {
907
0
            ReadLght_Binary(out, *reader, nfo);
908
0
        } else if (type == "Came") {
909
0
            ReadCame_Binary(out, *reader, nfo);
910
0
        } else if (type == "Mat1") {
911
0
            ReadMat1_Binary(out, *reader, nfo);
912
0
        } else if (type == "Unit") {
913
0
            ReadUnit_Binary(out, *reader, nfo);
914
0
        } else if (type == "OLay") {
915
            // ignore layer index silently.
916
0
            if (nfo.size != static_cast<unsigned int>(-1)) {
917
0
                reader->IncPtr(nfo.size);
918
0
            } else
919
0
                return UnsupportedChunk_Binary(*reader, nfo, type.c_str());
920
0
        } else if (type == "END ") {
921
0
            return;
922
0
        } else {
923
0
            UnsupportedChunk_Binary(*reader, nfo, type.c_str());
924
0
        }
925
0
    }
926
0
}
927
928
// ------------------------------------------------------------------------------------------------
929
0
void COBImporter::ReadPolH_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
930
0
    if (nfo.version > 8) {
931
0
        return UnsupportedChunk_Binary(reader, nfo, "PolH");
932
0
    }
933
0
    const chunk_guard cn(nfo, reader);
934
935
0
    out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh()));
936
0
    Mesh &msh = (Mesh &)(*out.nodes.back().get());
937
0
    msh = nfo;
938
939
0
    ReadBasicNodeInfo_Binary(msh, reader, nfo);
940
941
0
    msh.vertex_positions.resize(reader.GetI4());
942
0
    for (aiVector3D &v : msh.vertex_positions) {
943
0
        v.x = reader.GetF4();
944
0
        v.y = reader.GetF4();
945
0
        v.z = reader.GetF4();
946
0
    }
947
948
0
    msh.texture_coords.resize(reader.GetI4());
949
0
    for (aiVector2D &v : msh.texture_coords) {
950
0
        v.x = reader.GetF4();
951
0
        v.y = reader.GetF4();
952
0
    }
953
954
0
    const size_t numf = reader.GetI4();
955
0
    msh.faces.reserve(numf);
956
0
    for (size_t i = 0; i < numf; ++i) {
957
        // XXX backface culling flag is 0x10 in flags
958
959
        // hole?
960
0
        bool hole = (reader.GetI1() & 0x08) != 0;
961
0
        if (hole) {
962
            // XXX Basically this should just work fine - then triangulator
963
            // should output properly triangulated data even for polygons
964
            // with holes. Test data specific to COB is needed to confirm it.
965
0
            if (msh.faces.empty()) {
966
0
                ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id);
967
0
            }
968
0
        } else
969
0
            msh.faces.emplace_back();
970
0
        Face &f = msh.faces.back();
971
972
0
        const size_t num = reader.GetI2();
973
0
        f.indices.reserve(f.indices.size() + num);
974
975
0
        if (!hole) {
976
0
            f.material = reader.GetI2();
977
0
            f.flags = 0;
978
0
        }
979
980
0
        for (size_t x = 0; x < num; ++x) {
981
0
            f.indices.emplace_back();
982
983
0
            VertexIndex &v = f.indices.back();
984
0
            v.pos_idx = reader.GetI4();
985
0
            v.uv_idx = reader.GetI4();
986
0
        }
987
988
0
        if (hole) {
989
0
            std::reverse(f.indices.rbegin(), f.indices.rbegin() + num);
990
0
        }
991
0
    }
992
0
    if (nfo.version > 4) {
993
0
        msh.draw_flags = reader.GetI4();
994
0
    }
995
0
    nfo.version > 5 && nfo.version < 8 ? reader.GetI4() : 0;
996
0
}
997
998
// ------------------------------------------------------------------------------------------------
999
0
void COBImporter::ReadBitM_Binary(COB::Scene & /*out*/, StreamReaderLE &reader, const ChunkInfo &nfo) {
1000
0
    if (nfo.version > 1) {
1001
0
        return UnsupportedChunk_Binary(reader, nfo, "BitM");
1002
0
    }
1003
1004
0
    const chunk_guard cn(nfo, reader);
1005
1006
0
    const uint32_t len = reader.GetI4();
1007
0
    reader.IncPtr(len);
1008
1009
0
    reader.GetI4();
1010
0
    reader.IncPtr(reader.GetI4());
1011
0
}
1012
1013
// ------------------------------------------------------------------------------------------------
1014
0
void COBImporter::ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
1015
0
    if (nfo.version > 8) {
1016
0
        return UnsupportedChunk_Binary(reader, nfo, "Mat1");
1017
0
    }
1018
1019
0
    const chunk_guard cn(nfo, reader);
1020
1021
0
    out.materials.emplace_back();
1022
0
    Material &mat = out.materials.back();
1023
0
    mat = nfo;
1024
1025
0
    mat.matnum = reader.GetI2();
1026
0
    switch (reader.GetI1()) {
1027
0
    case 'f':
1028
0
        mat.type = Material::FLAT;
1029
0
        break;
1030
0
    case 'p':
1031
0
        mat.type = Material::PHONG;
1032
0
        break;
1033
0
    case 'm':
1034
0
        mat.type = Material::METAL;
1035
0
        break;
1036
0
    default:
1037
0
        ASSIMP_LOG_ERROR("Unrecognized shader type in `Mat1` chunk with id ", nfo.id);
1038
0
        mat.type = Material::FLAT;
1039
0
    }
1040
1041
0
    switch (reader.GetI1()) {
1042
0
    case 'f':
1043
0
        mat.autofacet = Material::FACETED;
1044
0
        break;
1045
0
    case 'a':
1046
0
        mat.autofacet = Material::AUTOFACETED;
1047
0
        break;
1048
0
    case 's':
1049
0
        mat.autofacet = Material::SMOOTH;
1050
0
        break;
1051
0
    default:
1052
0
        ASSIMP_LOG_ERROR("Unrecognized faceting mode in `Mat1` chunk with id ", nfo.id);
1053
0
        mat.autofacet = Material::FACETED;
1054
0
    }
1055
0
    mat.autofacet_angle = static_cast<float>(reader.GetI1());
1056
1057
0
    mat.rgb.r = reader.GetF4();
1058
0
    mat.rgb.g = reader.GetF4();
1059
0
    mat.rgb.b = reader.GetF4();
1060
1061
0
    mat.alpha = reader.GetF4();
1062
0
    mat.ka = reader.GetF4();
1063
0
    mat.ks = reader.GetF4();
1064
0
    mat.exp = reader.GetF4();
1065
0
    mat.ior = reader.GetF4();
1066
1067
0
    char id[2];
1068
0
    id[0] = reader.GetI1(), id[1] = reader.GetI1();
1069
1070
0
    if (id[0] == 'e' && id[1] == ':') {
1071
0
        mat.tex_env = std::make_shared<Texture>();
1072
1073
0
        reader.GetI1();
1074
0
        ReadString_Binary(mat.tex_env->path, reader);
1075
1076
        // advance to next texture-id
1077
0
        id[0] = reader.GetI1(), id[1] = reader.GetI1();
1078
0
    }
1079
1080
0
    if (id[0] == 't' && id[1] == ':') {
1081
0
        mat.tex_color = std::make_shared<Texture>();
1082
1083
0
        reader.GetI1();
1084
0
        ReadString_Binary(mat.tex_color->path, reader);
1085
1086
0
        mat.tex_color->transform.mTranslation.x = reader.GetF4();
1087
0
        mat.tex_color->transform.mTranslation.y = reader.GetF4();
1088
1089
0
        mat.tex_color->transform.mScaling.x = reader.GetF4();
1090
0
        mat.tex_color->transform.mScaling.y = reader.GetF4();
1091
1092
        // advance to next texture-id
1093
0
        id[0] = reader.GetI1(), id[1] = reader.GetI1();
1094
0
    }
1095
1096
0
    if (id[0] == 'b' && id[1] == ':') {
1097
0
        mat.tex_bump = std::make_shared<Texture>();
1098
1099
0
        reader.GetI1();
1100
0
        ReadString_Binary(mat.tex_bump->path, reader);
1101
1102
0
        mat.tex_bump->transform.mTranslation.x = reader.GetF4();
1103
0
        mat.tex_bump->transform.mTranslation.y = reader.GetF4();
1104
1105
0
        mat.tex_bump->transform.mScaling.x = reader.GetF4();
1106
0
        mat.tex_bump->transform.mScaling.y = reader.GetF4();
1107
1108
        // skip amplitude for I don't know its purpose.
1109
0
        reader.GetF4();
1110
0
    }
1111
0
    reader.IncPtr(-2);
1112
0
}
1113
1114
// ------------------------------------------------------------------------------------------------
1115
0
void COBImporter::ReadCame_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
1116
0
    if (nfo.version > 2) {
1117
0
        return UnsupportedChunk_Binary(reader, nfo, "Came");
1118
0
    }
1119
1120
0
    const chunk_guard cn(nfo, reader);
1121
1122
0
    out.nodes.push_back(std::shared_ptr<Camera>(new Camera()));
1123
0
    Camera &msh = (Camera &)(*out.nodes.back().get());
1124
0
    msh = nfo;
1125
1126
0
    ReadBasicNodeInfo_Binary(msh, reader, nfo);
1127
1128
    // the rest is not interesting for us, so we skip over it.
1129
0
    if (nfo.version > 1) {
1130
0
        if (reader.GetI2() == 512) {
1131
0
            reader.IncPtr(42);
1132
0
        }
1133
0
    }
1134
0
}
1135
1136
// ------------------------------------------------------------------------------------------------
1137
0
void COBImporter::ReadLght_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
1138
0
    if (nfo.version > 2) {
1139
0
        return UnsupportedChunk_Binary(reader, nfo, "Lght");
1140
0
    }
1141
1142
0
    const chunk_guard cn(nfo, reader);
1143
1144
0
    out.nodes.push_back(std::shared_ptr<Light>(new Light()));
1145
0
    Light &msh = (Light &)(*out.nodes.back().get());
1146
0
    msh = nfo;
1147
1148
0
    ReadBasicNodeInfo_Binary(msh, reader, nfo);
1149
0
}
1150
1151
// ------------------------------------------------------------------------------------------------
1152
0
void COBImporter::ReadGrou_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
1153
0
    if (nfo.version > 2) {
1154
0
        return UnsupportedChunk_Binary(reader, nfo, "Grou");
1155
0
    }
1156
1157
0
    const chunk_guard cn(nfo, reader);
1158
1159
0
    out.nodes.push_back(std::make_shared<Group>());
1160
0
    Group &msh = (Group &)(*out.nodes.back().get());
1161
0
    msh = nfo;
1162
1163
0
    ReadBasicNodeInfo_Binary(msh, reader, nfo);
1164
0
}
1165
1166
// ------------------------------------------------------------------------------------------------
1167
0
void COBImporter::ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const ChunkInfo &nfo) {
1168
0
    if (nfo.version > 1) {
1169
0
        return UnsupportedChunk_Binary(reader, nfo, "Unit");
1170
0
    }
1171
1172
0
    const chunk_guard cn(nfo, reader);
1173
1174
    // parent chunks preceede their children, so we should have the
1175
    // corresponding chunk already.
1176
0
    for (std::shared_ptr<Node> &nd : out.nodes) {
1177
0
        if (nd->id == nfo.parent_id) {
1178
0
            const unsigned int t = reader.GetI2();
1179
0
            nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? (
1180
0
                                                                             ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) :
1181
0
                                                                     units[t];
1182
1183
0
            return;
1184
0
        }
1185
0
    }
1186
0
    ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist");
1187
0
}
1188
1189
} // namespace Assimp
1190
1191
#endif // ASSIMP_BUILD_NO_COB_IMPORTER