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