Coverage Report

Created: 2026-05-23 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/B3D/B3DImporter.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
All rights reserved.
9
10
Redistribution and use of this software in source and binary forms,
11
with or without modification, are permitted provided that the following
12
conditions are met:
13
14
* Redistributions of source code must retain the above
15
  copyright notice, this list of conditions and the
16
  following disclaimer.
17
18
* Redistributions in binary form must reproduce the above
19
  copyright notice, this list of conditions and the
20
  following disclaimer in the documentation and/or other
21
  materials provided with the distribution.
22
23
* Neither the name of the assimp team, nor the names of its
24
  contributors may be used to endorse or promote products
25
  derived from this software without specific prior
26
  written permission of the assimp team.
27
28
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
---------------------------------------------------------------------------
40
*/
41
42
/** @file  B3DImporter.cpp
43
 *  @brief Implementation of the b3d importer class
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
47
48
// internal headers
49
#include "B3DImporter.h"
50
#include "PostProcessing/ConvertToLHProcess.h"
51
#include "PostProcessing/TextureTransform.h"
52
53
#include <assimp/StringUtils.h>
54
#include <assimp/anim.h>
55
#include <assimp/importerdesc.h>
56
#include <assimp/scene.h>
57
#include <assimp/DefaultLogger.hpp>
58
#include <assimp/IOSystem.hpp>
59
60
#include <memory>
61
62
namespace Assimp {
63
using namespace std;
64
65
static constexpr aiImporterDesc desc = {
66
    "BlitzBasic 3D Importer",
67
    "",
68
    "",
69
    "http://www.blitzbasic.com/",
70
    aiImporterFlags_SupportBinaryFlavour,
71
    0,
72
    0,
73
    0,
74
    0,
75
    "b3d"
76
};
77
78
#ifdef _MSC_VER
79
#pragma warning(disable : 4018)
80
#endif
81
82
// #define DEBUG_B3D
83
84
template <typename T>
85
0
void DeleteAllBarePointers(std::vector<T> &x) {
86
0
    for (auto p : x) {
87
0
        delete p;
88
0
    }
89
0
}
90
91
40.6k
B3DImporter::~B3DImporter() = default;
92
93
// ------------------------------------------------------------------------------------------------
94
0
bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
95
0
    size_t pos = pFile.find_last_of('.');
96
0
    if (pos == string::npos) {
97
0
        return false;
98
0
    }
99
100
0
    string ext = pFile.substr(pos + 1);
101
0
    if (ext.size() != 3) {
102
0
        return false;
103
0
    }
104
105
0
    return (ext[0] == 'b' || ext[0] == 'B') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D');
106
0
}
107
108
// ------------------------------------------------------------------------------------------------
109
// Loader meta information
110
40.6k
const aiImporterDesc *B3DImporter::GetInfo() const {
111
40.6k
    return &desc;
112
40.6k
}
113
114
// ------------------------------------------------------------------------------------------------
115
0
void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
116
0
    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
117
118
    // Check whether we can read from the file
119
0
    if (file == nullptr) {
120
0
        throw DeadlyImportError("Failed to open B3D file ", pFile, ".");
121
0
    }
122
123
    // check whether the .b3d file is large enough to contain
124
    // at least one chunk.
125
0
    size_t fileSize = file->FileSize();
126
0
    if (fileSize < 8) {
127
0
        throw DeadlyImportError("B3D File is too small.");
128
0
    }
129
130
0
    _pos = 0;
131
0
    _buf.resize(fileSize);
132
0
    file->Read(&_buf[0], 1, fileSize);
133
0
    _stack.clear();
134
135
0
    ReadBB3D(pScene);
136
0
}
137
138
// ------------------------------------------------------------------------------------------------
139
0
AI_WONT_RETURN void B3DImporter::Oops() {
140
0
    throw DeadlyImportError("B3D Importer - INTERNAL ERROR");
141
0
}
142
143
// ------------------------------------------------------------------------------------------------
144
0
AI_WONT_RETURN void B3DImporter::Fail(const string &str) {
145
#ifdef DEBUG_B3D
146
    ASSIMP_LOG_ERROR("Error in B3D file data: ", str);
147
#endif
148
0
    throw DeadlyImportError("B3D Importer - error in B3D file data: ", str);
149
0
}
150
151
// ------------------------------------------------------------------------------------------------
152
0
int B3DImporter::ReadByte() {
153
0
    if (_pos >= _buf.size()) {
154
0
        Fail("EOF");
155
0
    }
156
157
0
    return _buf[_pos++];
158
0
}
159
160
// ------------------------------------------------------------------------------------------------
161
0
int B3DImporter::ReadInt() {
162
0
    if (_pos + 4 > _buf.size()) {
163
0
        Fail("EOF");
164
0
    }
165
166
0
    int n;
167
0
    memcpy(&n, &_buf[_pos], 4);
168
0
    _pos += 4;
169
170
0
    return n;
171
0
}
172
173
// ------------------------------------------------------------------------------------------------
174
0
float B3DImporter::ReadFloat() {
175
0
    if (_pos + 4 > _buf.size()) {
176
0
        Fail("EOF");
177
0
    }
178
179
0
    float n;
180
0
    memcpy(&n, &_buf[_pos], 4);
181
0
    _pos += 4;
182
183
0
    return n;
184
0
}
185
186
// ------------------------------------------------------------------------------------------------
187
0
aiVector2D B3DImporter::ReadVec2() {
188
0
    float x = ReadFloat();
189
0
    float y = ReadFloat();
190
0
    return aiVector2D(x, y);
191
0
}
192
193
// ------------------------------------------------------------------------------------------------
194
0
aiVector3D B3DImporter::ReadVec3() {
195
0
    float x = ReadFloat();
196
0
    float y = ReadFloat();
197
0
    float z = ReadFloat();
198
0
    return aiVector3D(x, y, z);
199
0
}
200
201
// ------------------------------------------------------------------------------------------------
202
0
aiQuaternion B3DImporter::ReadQuat() {
203
    // (aramis_acg) Fix to adapt the loader to changed quat orientation
204
0
    float w = -ReadFloat();
205
0
    float x = ReadFloat();
206
0
    float y = ReadFloat();
207
0
    float z = ReadFloat();
208
0
    return aiQuaternion(w, x, y, z);
209
0
}
210
211
// ------------------------------------------------------------------------------------------------
212
0
string B3DImporter::ReadString() {
213
0
    if (_pos > _buf.size()) {
214
0
        Fail("EOF");
215
0
    }
216
0
    string str;
217
0
    while (_pos < _buf.size()) {
218
0
        char c = (char)ReadByte();
219
0
        if (!c) {
220
0
            return str;
221
0
        }
222
0
        str += c;
223
0
    }
224
0
    return string();
225
0
}
226
227
// ------------------------------------------------------------------------------------------------
228
0
string B3DImporter::ReadChunk() {
229
0
    string tag;
230
0
    for (int i = 0; i < 4; ++i) {
231
0
        tag += char(ReadByte());
232
0
    }
233
#ifdef DEBUG_B3D
234
    ASSIMP_LOG_DEBUG("ReadChunk: ", tag);
235
#endif
236
0
    unsigned sz = (unsigned)ReadInt();
237
0
    _stack.push_back(_pos + sz);
238
0
    return tag;
239
0
}
240
241
// ------------------------------------------------------------------------------------------------
242
0
void B3DImporter::ExitChunk() {
243
0
    _pos = _stack.back();
244
0
    _stack.pop_back();
245
0
}
246
247
// ------------------------------------------------------------------------------------------------
248
0
size_t B3DImporter::ChunkSize() {
249
0
    return _stack.back() - _pos;
250
0
}
251
// ------------------------------------------------------------------------------------------------
252
253
template <class T>
254
0
T *B3DImporter::to_array(const vector<T> &v) {
255
0
    if (v.empty()) {
256
0
        return nullptr;
257
0
    }
258
0
    T *p = new T[v.size()];
259
0
    for (size_t i = 0; i < v.size(); ++i) {
260
0
        p[i] = v[i];
261
0
    }
262
0
    return p;
263
0
}
Unexecuted instantiation: aiVectorKey* Assimp::B3DImporter::to_array<aiVectorKey>(std::__1::vector<aiVectorKey, std::__1::allocator<aiVectorKey> > const&)
Unexecuted instantiation: aiQuatKey* Assimp::B3DImporter::to_array<aiQuatKey>(std::__1::vector<aiQuatKey, std::__1::allocator<aiQuatKey> > const&)
Unexecuted instantiation: unsigned int* Assimp::B3DImporter::to_array<unsigned int>(std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
Unexecuted instantiation: aiNode** Assimp::B3DImporter::to_array<aiNode*>(std::__1::vector<aiNode*, std::__1::allocator<aiNode*> > const&)
Unexecuted instantiation: aiVertexWeight* Assimp::B3DImporter::to_array<aiVertexWeight>(std::__1::vector<aiVertexWeight, std::__1::allocator<aiVertexWeight> > const&)
Unexecuted instantiation: aiBone** Assimp::B3DImporter::to_array<aiBone*>(std::__1::vector<aiBone*, std::__1::allocator<aiBone*> > const&)
264
265
// ------------------------------------------------------------------------------------------------
266
template <class T>
267
0
T **unique_to_array(vector<std::unique_ptr<T>> &v) {
268
0
    if (v.empty()) {
269
0
        return nullptr;
270
0
    }
271
0
    T **p = new T *[v.size()];
272
0
    for (size_t i = 0; i < v.size(); ++i) {
273
0
        p[i] = v[i].release();
274
0
    }
275
0
    return p;
276
0
}
Unexecuted instantiation: aiMaterial** Assimp::unique_to_array<aiMaterial>(std::__1::vector<std::__1::unique_ptr<aiMaterial, std::__1::default_delete<aiMaterial> >, std::__1::allocator<std::__1::unique_ptr<aiMaterial, std::__1::default_delete<aiMaterial> > > >&)
Unexecuted instantiation: aiMesh** Assimp::unique_to_array<aiMesh>(std::__1::vector<std::__1::unique_ptr<aiMesh, std::__1::default_delete<aiMesh> >, std::__1::allocator<std::__1::unique_ptr<aiMesh, std::__1::default_delete<aiMesh> > > >&)
Unexecuted instantiation: aiNodeAnim** Assimp::unique_to_array<aiNodeAnim>(std::__1::vector<std::__1::unique_ptr<aiNodeAnim, std::__1::default_delete<aiNodeAnim> >, std::__1::allocator<std::__1::unique_ptr<aiNodeAnim, std::__1::default_delete<aiNodeAnim> > > >&)
Unexecuted instantiation: aiAnimation** Assimp::unique_to_array<aiAnimation>(std::__1::vector<std::__1::unique_ptr<aiAnimation, std::__1::default_delete<aiAnimation> >, std::__1::allocator<std::__1::unique_ptr<aiAnimation, std::__1::default_delete<aiAnimation> > > >&)
277
278
// ------------------------------------------------------------------------------------------------
279
0
void B3DImporter::ReadTEXS() {
280
0
    while (ChunkSize()) {
281
0
        string name = ReadString();
282
0
        /*int flags=*/ReadInt();
283
0
        /*int blend=*/ReadInt();
284
0
        /*aiVector2D pos=*/ReadVec2();
285
0
        /*aiVector2D scale=*/ReadVec2();
286
0
        /*float rot=*/ReadFloat();
287
288
0
        _textures.push_back(name);
289
0
    }
290
0
}
291
292
// ------------------------------------------------------------------------------------------------
293
0
void B3DImporter::ReadBRUS() {
294
0
    int n_texs = ReadInt();
295
0
    if (n_texs < 0 || n_texs > 8) {
296
0
        Fail("Bad texture count");
297
0
    }
298
0
    while (ChunkSize()) {
299
0
        string name = ReadString();
300
0
        aiVector3D color = ReadVec3();
301
0
        float alpha = ReadFloat();
302
0
        float shiny = ReadFloat();
303
0
        /*int blend=**/ ReadInt();
304
0
        int fx = ReadInt();
305
306
0
        std::unique_ptr<aiMaterial> mat(new aiMaterial);
307
308
        // Name
309
0
        aiString ainame(name);
310
0
        mat->AddProperty(&ainame, AI_MATKEY_NAME);
311
312
        // Diffuse color
313
0
        mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE);
314
315
        // Opacity
316
0
        mat->AddProperty(&alpha, 1, AI_MATKEY_OPACITY);
317
318
        // Specular color
319
0
        aiColor3D speccolor(shiny, shiny, shiny);
320
0
        mat->AddProperty(&speccolor, 1, AI_MATKEY_COLOR_SPECULAR);
321
322
        // Specular power
323
0
        float specpow = shiny * 128;
324
0
        mat->AddProperty(&specpow, 1, AI_MATKEY_SHININESS);
325
326
        // Double sided
327
0
        if (fx & 0x10) {
328
0
            int i = 1;
329
0
            mat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
330
0
        }
331
332
        // Textures
333
0
        for (int i = 0; i < n_texs; ++i) {
334
0
            int texid = ReadInt();
335
0
            if (texid < -1 || (texid >= 0 && texid >= static_cast<int>(_textures.size()))) {
336
0
                Fail("Bad texture id");
337
0
            }
338
0
            if (i == 0 && texid >= 0) {
339
0
                aiString texname(_textures[texid]);
340
0
                mat->AddProperty(&texname, AI_MATKEY_TEXTURE_DIFFUSE(0));
341
0
            }
342
0
        }
343
0
        _materials.emplace_back(std::move(mat));
344
0
    }
345
0
}
346
347
// ------------------------------------------------------------------------------------------------
348
0
void B3DImporter::ReadVRTS() {
349
0
    _vflags = ReadInt();
350
0
    _tcsets = ReadInt();
351
0
    _tcsize = ReadInt();
352
0
    if (_tcsets < 0 || _tcsets > 4 || _tcsize < 0 || _tcsize > 4) {
353
0
        Fail("Bad texcoord data");
354
0
    }
355
356
0
    int sz = 12 + (_vflags & 1 ? 12 : 0) + (_vflags & 2 ? 16 : 0) + (_tcsets * _tcsize * 4);
357
0
    size_t n_verts = ChunkSize() / sz;
358
359
0
    int v0 = static_cast<int>(_vertices.size());
360
0
    _vertices.resize(v0 + n_verts);
361
362
0
    for (unsigned int i = 0; i < n_verts; ++i) {
363
0
        Vertex &v = _vertices[v0 + i];
364
365
0
        memset(v.bones, 0, sizeof(v.bones));
366
0
        memset(v.weights, 0, sizeof(v.weights));
367
368
0
        v.vertex = ReadVec3();
369
370
0
        if (_vflags & 1) {
371
0
            v.normal = ReadVec3();
372
0
        }
373
374
0
        if (_vflags & 2) {
375
0
            ReadQuat(); // skip v 4bytes...
376
0
        }
377
378
0
        for (int j = 0; j < _tcsets; ++j) {
379
0
            float t[4] = { 0, 0, 0, 0 };
380
0
            for (int k = 0; k < _tcsize; ++k) {
381
0
                t[k] = ReadFloat();
382
0
            }
383
0
            t[1] = 1 - t[1];
384
0
            if (!j) {
385
0
                v.texcoords = aiVector3D(t[0], t[1], t[2]);
386
0
            }
387
0
        }
388
0
    }
389
0
}
390
391
// ------------------------------------------------------------------------------------------------
392
0
void B3DImporter::ReadTRIS(int v0) {
393
0
    int matid = ReadInt();
394
0
    if (matid == -1) {
395
0
        matid = 0;
396
0
    } else if (matid < 0 || matid >= (int)_materials.size()) {
397
#ifdef DEBUG_B3D
398
        ASSIMP_LOG_ERROR("material id=", matid);
399
#endif
400
0
        Fail("Bad material id");
401
0
    }
402
403
0
    std::unique_ptr<aiMesh> mesh(new aiMesh);
404
405
0
    mesh->mMaterialIndex = matid;
406
0
    mesh->mNumFaces = 0;
407
0
    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
408
409
0
    size_t n_tris = ChunkSize() / 12;
410
0
    aiFace *face = mesh->mFaces = new aiFace[n_tris];
411
412
0
    for (unsigned int i = 0; i < n_tris; ++i) {
413
0
        int i0 = ReadInt() + v0;
414
0
        int i1 = ReadInt() + v0;
415
0
        int i2 = ReadInt() + v0;
416
0
        if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
417
#ifdef DEBUG_B3D
418
            ASSIMP_LOG_ERROR("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
419
#endif
420
0
            Fail("Bad triangle index");
421
0
        }
422
0
        face->mNumIndices = 3;
423
0
        face->mIndices = new unsigned[3];
424
0
        face->mIndices[0] = i0;
425
0
        face->mIndices[1] = i1;
426
0
        face->mIndices[2] = i2;
427
0
        ++mesh->mNumFaces;
428
0
        ++face;
429
0
    }
430
431
0
    _meshes.emplace_back(std::move(mesh));
432
0
}
433
434
// ------------------------------------------------------------------------------------------------
435
0
void B3DImporter::ReadMESH() {
436
0
    /*int matid=*/ReadInt();
437
438
0
    int v0 = static_cast<int>(_vertices.size());
439
440
0
    while (ChunkSize()) {
441
0
        string t = ReadChunk();
442
0
        if (t == "VRTS") {
443
0
            ReadVRTS();
444
0
        } else if (t == "TRIS") {
445
0
            ReadTRIS(v0);
446
0
        }
447
0
        ExitChunk();
448
0
    }
449
0
}
450
451
// ------------------------------------------------------------------------------------------------
452
0
void B3DImporter::ReadBONE(int id) {
453
0
    while (ChunkSize()) {
454
0
        int vertex = ReadInt();
455
0
        float weight = ReadFloat();
456
0
        if (vertex < 0 || vertex >= (int)_vertices.size()) {
457
0
            Fail("Bad vertex index");
458
0
        }
459
460
0
        Vertex &v = _vertices[vertex];
461
0
        for (int i = 0; i < 4; ++i) {
462
0
            if (!v.weights[i]) {
463
0
                v.bones[i] = static_cast<unsigned char>(id);
464
0
                v.weights[i] = weight;
465
0
                break;
466
0
            }
467
0
        }
468
0
    }
469
0
}
470
471
// ------------------------------------------------------------------------------------------------
472
0
void B3DImporter::ReadKEYS(AnimKeys& keys) {
473
0
    vector<aiVectorKey>& trans = keys.positionKeys;
474
0
    vector<aiVectorKey>& scale = keys.scalingKeys;
475
0
    vector<aiQuatKey>& rot = keys.rotationKeys;
476
477
0
    int flags = ReadInt();
478
0
    while (ChunkSize()) {
479
0
        int frame = ReadInt();
480
0
        if (flags & 1) {
481
0
            trans.emplace_back(frame, ReadVec3());
482
0
        }
483
0
        if (flags & 2) {
484
0
            scale.emplace_back(frame, ReadVec3());
485
0
        }
486
0
        if (flags & 4) {
487
0
            rot.emplace_back(frame, ReadQuat());
488
0
        }
489
0
    }
490
0
}
491
492
// ------------------------------------------------------------------------------------------------
493
0
void B3DImporter::ReadANIM() {
494
0
    /*int flags=*/ReadInt();
495
0
    int frames = ReadInt();
496
0
    float fps = ReadFloat();
497
498
0
    std::unique_ptr<aiAnimation> anim(new aiAnimation);
499
500
0
    anim->mDuration = frames;
501
0
    anim->mTicksPerSecond = fps;
502
0
    _animations.emplace_back(std::move(anim));
503
0
}
504
505
// ------------------------------------------------------------------------------------------------
506
0
aiNode *B3DImporter::ReadNODE(aiNode *parent) {
507
508
0
    string name = ReadString();
509
0
    aiVector3D t = ReadVec3();
510
0
    aiVector3D s = ReadVec3();
511
0
    aiQuaternion r = ReadQuat();
512
513
0
    aiMatrix4x4 trans, scale, rot;
514
515
0
    aiMatrix4x4::Translation(t, trans);
516
0
    aiMatrix4x4::Scaling(s, scale);
517
0
    rot = aiMatrix4x4(r.GetMatrix());
518
519
0
    aiMatrix4x4 tform = trans * rot * scale;
520
521
0
    int nodeid = static_cast<int>(_nodes.size());
522
523
0
    aiNode *node = new aiNode(name);
524
0
    _nodes.push_back(node);
525
526
0
    node->mParent = parent;
527
0
    node->mTransformation = tform;
528
529
0
    std::unique_ptr<aiNodeAnim> nodeAnim;
530
0
    vector<unsigned> meshes;
531
0
    vector<aiNode *> children;
532
0
    AnimKeys keys;
533
534
0
    while (ChunkSize()) {
535
0
        const string chunk = ReadChunk();
536
0
        if (chunk == "MESH") {
537
0
            unsigned int n = static_cast<unsigned int>(_meshes.size());
538
0
            ReadMESH();
539
0
            for (unsigned int i = n; i < static_cast<unsigned int>(_meshes.size()); ++i) {
540
0
                meshes.push_back(i);
541
0
            }
542
0
        } else if (chunk == "BONE") {
543
0
            ReadBONE(nodeid);
544
0
        } else if (chunk == "ANIM") {
545
0
            ReadANIM();
546
0
        } else if (chunk == "KEYS") {
547
0
            if (!nodeAnim) {
548
0
                nodeAnim.reset(new aiNodeAnim);
549
0
                nodeAnim->mNodeName = node->mName;
550
0
            }
551
0
            ReadKEYS(keys);
552
0
        } else if (chunk == "NODE") {
553
0
            aiNode *child = ReadNODE(node);
554
0
            children.push_back(child);
555
0
        }
556
0
        ExitChunk();
557
0
    }
558
559
0
    if (nodeAnim) {
560
0
        if (!keys.positionKeys.empty()) {
561
0
            nodeAnim->mNumPositionKeys = static_cast<unsigned int>(keys.positionKeys.size());
562
0
            nodeAnim->mPositionKeys = to_array(keys.positionKeys);
563
0
        }
564
565
0
        if (!keys.scalingKeys.empty()) {
566
0
            nodeAnim->mNumScalingKeys = static_cast<unsigned int>(keys.scalingKeys.size());
567
0
            nodeAnim->mScalingKeys = to_array(keys.scalingKeys);
568
0
        }
569
570
0
        if (!keys.rotationKeys.empty()) {
571
0
            nodeAnim->mNumRotationKeys = static_cast<unsigned int>(keys.rotationKeys.size());
572
0
            nodeAnim->mRotationKeys = to_array(keys.rotationKeys);
573
0
        }
574
575
0
        _nodeAnims.emplace_back(std::move(nodeAnim));
576
0
    }
577
578
0
    node->mNumMeshes = static_cast<unsigned int>(meshes.size());
579
0
    node->mMeshes = to_array(meshes);
580
581
0
    node->mNumChildren = static_cast<unsigned int>(children.size());
582
0
    node->mChildren = to_array(children);
583
584
0
    return node;
585
0
}
586
587
// ------------------------------------------------------------------------------------------------
588
0
void B3DImporter::ReadBB3D(aiScene *scene) {
589
590
0
    _textures.clear();
591
592
0
    _materials.clear();
593
594
0
    _vertices.clear();
595
596
0
    _meshes.clear();
597
598
0
    DeleteAllBarePointers(_nodes);
599
0
    _nodes.clear();
600
601
0
    _nodeAnims.clear();
602
603
0
    _animations.clear();
604
605
0
    string t = ReadChunk();
606
0
    if (t == "BB3D") {
607
0
        int version = ReadInt();
608
609
0
        if (!DefaultLogger::isNullLogger()) {
610
0
            char dmp[128];
611
0
            ai_snprintf(dmp, 128, "B3D file format version: %i", version);
612
0
            ASSIMP_LOG_INFO(dmp);
613
0
        }
614
615
0
        while (ChunkSize()) {
616
0
            const string chunk = ReadChunk();
617
0
            if (chunk == "TEXS") {
618
0
                ReadTEXS();
619
0
            } else if (chunk == "BRUS") {
620
0
                ReadBRUS();
621
0
            } else if (chunk == "NODE") {
622
0
                ReadNODE(nullptr);
623
0
            }
624
0
            ExitChunk();
625
0
        }
626
0
    }
627
0
    ExitChunk();
628
629
0
    if (!_nodes.size()) {
630
0
        Fail("No nodes");
631
0
    }
632
633
0
    if (!_meshes.size()) {
634
0
        Fail("No meshes");
635
0
    }
636
637
    // Fix nodes/meshes/bones
638
0
    for (size_t i = 0; i < _nodes.size(); ++i) {
639
0
        aiNode *node = _nodes[i];
640
641
0
        for (size_t j = 0; j < node->mNumMeshes; ++j) {
642
0
            aiMesh *mesh = _meshes[node->mMeshes[j]].get();
643
644
0
            int n_tris = mesh->mNumFaces;
645
0
            int n_verts = mesh->mNumVertices = n_tris * 3;
646
647
0
            aiVector3D *mv = mesh->mVertices = new aiVector3D[n_verts], *mn = nullptr, *mc = nullptr;
648
0
            if (_vflags & 1) {
649
0
                mn = mesh->mNormals = new aiVector3D[n_verts];
650
0
            }
651
0
            if (_tcsets) {
652
0
                mc = mesh->mTextureCoords[0] = new aiVector3D[n_verts];
653
0
            }
654
655
0
            aiFace *face = mesh->mFaces;
656
657
0
            vector<vector<aiVertexWeight>> vweights(_nodes.size());
658
659
0
            for (int vertIdx = 0; vertIdx < n_verts; vertIdx += 3) {
660
0
                for (int faceIndex = 0; faceIndex < 3; ++faceIndex) {
661
0
                    Vertex &v = _vertices[face->mIndices[faceIndex]];
662
663
0
                    *mv++ = v.vertex;
664
0
                    if (mn) *mn++ = v.normal;
665
0
                    if (mc) *mc++ = v.texcoords;
666
667
0
                    face->mIndices[faceIndex] = vertIdx + faceIndex;
668
669
0
                    for (int k = 0; k < 4; ++k) {
670
0
                        if (!v.weights[k])
671
0
                            break;
672
673
0
                        int bone = v.bones[k];
674
0
                        float weight = v.weights[k];
675
676
0
                        vweights[bone].emplace_back(vertIdx + faceIndex, weight);
677
0
                    }
678
0
                }
679
0
                ++face;
680
0
            }
681
682
0
            vector<aiBone *> bones;
683
0
            for (size_t weightIndx = 0; weightIndx < vweights.size(); ++weightIndx) {
684
0
                vector<aiVertexWeight> &weights = vweights[weightIndx];
685
0
                if (!weights.size()) {
686
0
                    continue;
687
0
                }
688
689
0
                aiBone *bone = new aiBone;
690
0
                bones.push_back(bone);
691
692
0
                aiNode *bnode = _nodes[weightIndx];
693
694
0
                bone->mName = bnode->mName;
695
0
                bone->mNumWeights = static_cast<unsigned int>(weights.size());
696
0
                bone->mWeights = to_array(weights);
697
698
0
                aiMatrix4x4 mat = bnode->mTransformation;
699
0
                while (bnode->mParent) {
700
0
                    bnode = bnode->mParent;
701
0
                    mat = bnode->mTransformation * mat;
702
0
                }
703
0
                bone->mOffsetMatrix = mat.Inverse();
704
0
            }
705
0
            mesh->mNumBones = static_cast<unsigned int>(bones.size());
706
0
            mesh->mBones = to_array(bones);
707
0
        }
708
0
    }
709
710
    // nodes
711
0
    scene->mRootNode = _nodes[0];
712
0
    _nodes.clear(); // node ownership now belongs to scene
713
714
    // material
715
0
    if (!_materials.size()) {
716
0
        _materials.emplace_back(std::unique_ptr<aiMaterial>(new aiMaterial));
717
0
    }
718
0
    scene->mNumMaterials = static_cast<unsigned int>(_materials.size());
719
0
    scene->mMaterials = unique_to_array(_materials);
720
721
    // meshes
722
0
    scene->mNumMeshes = static_cast<unsigned int>(_meshes.size());
723
0
    scene->mMeshes = unique_to_array(_meshes);
724
725
    // animations
726
0
    if (_animations.size() == 1 && _nodeAnims.size()) {
727
728
0
        aiAnimation *anim = _animations.back().get();
729
0
        anim->mNumChannels = static_cast<unsigned int>(_nodeAnims.size());
730
0
        anim->mChannels = unique_to_array(_nodeAnims);
731
732
0
        scene->mNumAnimations = static_cast<unsigned int>(_animations.size());
733
0
        scene->mAnimations = unique_to_array(_animations);
734
0
    }
735
736
    // convert to RH
737
0
    MakeLeftHandedProcess makeleft;
738
0
    makeleft.Execute(scene);
739
740
0
    FlipWindingOrderProcess flip;
741
0
    flip.Execute(scene);
742
0
}
743
744
} // namespace Assimp
745
746
#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER