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