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