1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2019, 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 
47 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
48 
49 // internal headers
50 #include "B3D/B3DImporter.h"
51 #include "PostProcessing/TextureTransform.h"
52 #include "PostProcessing/ConvertToLHProcess.h"
53 
54 #include <assimp/StringUtils.h>
55 #include <assimp/IOSystem.hpp>
56 #include <assimp/anim.h>
57 #include <assimp/scene.h>
58 #include <assimp/DefaultLogger.hpp>
59 #include <assimp/importerdesc.h>
60 
61 #include <memory>
62 
63 using namespace Assimp;
64 using namespace std;
65 
66 static const aiImporterDesc desc = {
67     "BlitzBasic 3D Importer",
68     "",
69     "",
70     "http://www.blitzbasic.com/",
71     aiImporterFlags_SupportBinaryFlavour,
72     0,
73     0,
74     0,
75     0,
76     "b3d"
77 };
78 
79 #ifdef _MSC_VER
80 #	pragma warning (disable: 4018)
81 #endif
82 
83 //#define DEBUG_B3D
84 
85 template<typename T>
DeleteAllBarePointers(std::vector<T> & x)86 void DeleteAllBarePointers(std::vector<T>& x) {
87     for(auto p : x) {
88         delete p;
89     }
90 }
91 
~B3DImporter()92 B3DImporter::~B3DImporter()
93 {
94 }
95 
96 // ------------------------------------------------------------------------------------------------
CanRead(const std::string & pFile,IOSystem *,bool) const97 bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{
98 
99     size_t pos=pFile.find_last_of( '.' );
100     if( pos==string::npos ) {
101         return false;
102     }
103 
104     string ext=pFile.substr( pos+1 );
105     if( ext.size()!=3 ) {
106         return false;
107     }
108 
109     return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
110 }
111 
112 // ------------------------------------------------------------------------------------------------
113 // Loader meta information
GetInfo() const114 const aiImporterDesc* B3DImporter::GetInfo () const
115 {
116     return &desc;
117 }
118 
119 // ------------------------------------------------------------------------------------------------
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)120 void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
121     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
122 
123     // Check whether we can read from the file
124     if( file.get() == nullptr) {
125         throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
126     }
127 
128     // check whether the .b3d file is large enough to contain
129     // at least one chunk.
130     size_t fileSize = file->FileSize();
131     if( fileSize<8 ) {
132         throw DeadlyImportError( "B3D File is too small.");
133     }
134 
135     _pos=0;
136     _buf.resize( fileSize );
137     file->Read( &_buf[0],1,fileSize );
138     _stack.clear();
139 
140     ReadBB3D( pScene );
141 }
142 
143 // ------------------------------------------------------------------------------------------------
Oops()144 AI_WONT_RETURN void B3DImporter::Oops(){
145     throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" );
146 }
147 
148 // ------------------------------------------------------------------------------------------------
Fail(string str)149 AI_WONT_RETURN void B3DImporter::Fail( string str ){
150 #ifdef DEBUG_B3D
151     ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
152 #endif
153     throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
154 }
155 
156 // ------------------------------------------------------------------------------------------------
ReadByte()157 int B3DImporter::ReadByte(){
158     if( _pos<_buf.size() ) {
159         return _buf[_pos++];
160     }
161 
162     Fail( "EOF" );
163     return 0;
164 }
165 
166 // ------------------------------------------------------------------------------------------------
ReadInt()167 int B3DImporter::ReadInt(){
168     if( _pos+4<=_buf.size() ){
169         int n;
170         memcpy(&n, &_buf[_pos], 4);
171         _pos+=4;
172         return n;
173     }
174     Fail( "EOF" );
175     return 0;
176 }
177 
178 // ------------------------------------------------------------------------------------------------
ReadFloat()179 float B3DImporter::ReadFloat(){
180     if( _pos+4<=_buf.size() ){
181         float n;
182         memcpy(&n, &_buf[_pos], 4);
183         _pos+=4;
184         return n;
185     }
186     Fail( "EOF" );
187     return 0.0f;
188 }
189 
190 // ------------------------------------------------------------------------------------------------
ReadVec2()191 aiVector2D B3DImporter::ReadVec2(){
192     float x=ReadFloat();
193     float y=ReadFloat();
194     return aiVector2D( x,y );
195 }
196 
197 // ------------------------------------------------------------------------------------------------
ReadVec3()198 aiVector3D B3DImporter::ReadVec3(){
199     float x=ReadFloat();
200     float y=ReadFloat();
201     float z=ReadFloat();
202     return aiVector3D( x,y,z );
203 }
204 
205 // ------------------------------------------------------------------------------------------------
ReadQuat()206 aiQuaternion B3DImporter::ReadQuat(){
207     // (aramis_acg) Fix to adapt the loader to changed quat orientation
208     float w=-ReadFloat();
209     float x=ReadFloat();
210     float y=ReadFloat();
211     float z=ReadFloat();
212     return aiQuaternion( w,x,y,z );
213 }
214 
215 // ------------------------------------------------------------------------------------------------
ReadString()216 string B3DImporter::ReadString(){
217     string str;
218     while( _pos<_buf.size() ){
219         char c=(char)ReadByte();
220         if( !c ) {
221             return str;
222         }
223         str+=c;
224     }
225     Fail( "EOF" );
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_F("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 unsigned 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     int n_verts=ChunkSize()/sz;
360 
361     int v0=static_cast<int>(_vertices.size());
362     _vertices.resize( v0+n_verts );
363 
364     for( 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 i=0;i<_tcsets;++i ){
381             float t[4]={0,0,0,0};
382             for( int j=0;j<_tcsize;++j ){
383                 t[j]=ReadFloat();
384             }
385             t[1]=1-t[1];
386             if( !i ) {
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_F("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 	int n_tris = ChunkSize() / 12;
412 	aiFace *face = mesh->mFaces = new aiFace[n_tris];
413 
414 	for (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_F("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] = 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         string t=ReadChunk();
551         if( t=="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( t=="BONE" ){
558             ReadBONE( nodeid );
559         }else if( t=="ANIM" ){
560             ReadANIM();
561         }else if( t=="KEYS" ){
562             if( !nodeAnim ){
563                 nodeAnim.reset(new aiNodeAnim);
564                 nodeAnim->mNodeName=node->mName;
565             }
566             ReadKEYS( nodeAnim.get() );
567         }else if( t=="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             string t=ReadChunk();
617             if( t=="TEXS" ){
618                 ReadTEXS();
619             }else if( t=="BRUS" ){
620                 ReadBRUS();
621             }else if( t=="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 i=0;i<n_verts;i+=3 ){
660                 for( int j=0;j<3;++j ){
661                     Vertex &v=_vertices[face->mIndices[j]];
662 
663                     *mv++=v.vertex;
664                     if( mn ) *mn++=v.normal;
665                     if( mc ) *mc++=v.texcoords;
666 
667                     face->mIndices[j]=i+j;
668 
669                     for( int k=0;k<4;++k ){
670                         if( !v.weights[k] ) break;
671 
672                         int bone=v.bones[k];
673                         float weight=v.weights[k];
674 
675                         vweights[bone].push_back( aiVertexWeight(i+j,weight) );
676                     }
677                 }
678                 ++face;
679             }
680 
681             vector<aiBone*> bones;
682             for(size_t i=0;i<vweights.size();++i ){
683                 vector<aiVertexWeight> &weights=vweights[i];
684                 if( !weights.size() ) continue;
685 
686                 aiBone *bone=new aiBone;
687                 bones.push_back( bone );
688 
689                 aiNode *bnode=_nodes[i];
690 
691                 bone->mName=bnode->mName;
692                 bone->mNumWeights= static_cast<unsigned int>(weights.size());
693                 bone->mWeights=to_array( weights );
694 
695                 aiMatrix4x4 mat=bnode->mTransformation;
696                 while( bnode->mParent ){
697                     bnode=bnode->mParent;
698                     mat=bnode->mTransformation * mat;
699                 }
700                 bone->mOffsetMatrix=mat.Inverse();
701             }
702             mesh->mNumBones= static_cast<unsigned int>(bones.size());
703             mesh->mBones=to_array( bones );
704         }
705     }
706 
707     //nodes
708     scene->mRootNode=_nodes[0];
709     _nodes.clear();  // node ownership now belongs to scene
710 
711     //material
712     if( !_materials.size() ){
713         _materials.emplace_back( std::unique_ptr<aiMaterial>(new aiMaterial) );
714     }
715     scene->mNumMaterials= static_cast<unsigned int>(_materials.size());
716     scene->mMaterials = unique_to_array( _materials );
717 
718     //meshes
719     scene->mNumMeshes= static_cast<unsigned int>(_meshes.size());
720     scene->mMeshes = unique_to_array( _meshes );
721 
722     //animations
723     if( _animations.size()==1 && _nodeAnims.size() ){
724 
725         aiAnimation *anim = _animations.back().get();
726         anim->mNumChannels=static_cast<unsigned int>(_nodeAnims.size());
727         anim->mChannels = unique_to_array( _nodeAnims );
728 
729         scene->mNumAnimations=static_cast<unsigned int>(_animations.size());
730         scene->mAnimations=unique_to_array( _animations );
731     }
732 
733     // convert to RH
734     MakeLeftHandedProcess makeleft;
735     makeleft.Execute( scene );
736 
737     FlipWindingOrderProcess flip;
738     flip.Execute( scene );
739 }
740 
741 #endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER
742