1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions 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
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF 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
62using namespace Assimp;
63using namespace std;
64
65static 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
85template<typename T>
86void DeleteAllBarePointers(std::vector<T>& x)
87{
88 for(auto p : x)
89 {
90 delete p;
91 }
92}
93
94B3DImporter::~B3DImporter()
95{
96 DeleteAllBarePointers(_animations);
97}
98
99// ------------------------------------------------------------------------------------------------
100bool 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
113const aiImporterDesc* B3DImporter::GetInfo () const
114{
115 return &desc;
116}
117
118#ifdef DEBUG_B3D
119 extern "C"{ void _stdcall AllocConsole(); }
120#endif
121// ------------------------------------------------------------------------------------------------
122void 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// ------------------------------------------------------------------------------------------------
152AI_WONT_RETURN void B3DImporter::Oops(){
153 throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" );
154}
155
156// ------------------------------------------------------------------------------------------------
157AI_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// ------------------------------------------------------------------------------------------------
165int B3DImporter::ReadByte(){
166 if( _pos<_buf.size() ) return _buf[_pos++];
167 Fail( "EOF" );
168 return 0;
169}
170
171// ------------------------------------------------------------------------------------------------
172int 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// ------------------------------------------------------------------------------------------------
184float 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// ------------------------------------------------------------------------------------------------
196aiVector2D B3DImporter::ReadVec2(){
197 float x=ReadFloat();
198 float y=ReadFloat();
199 return aiVector2D( x,y );
200}
201
202// ------------------------------------------------------------------------------------------------
203aiVector3D B3DImporter::ReadVec3(){
204 float x=ReadFloat();
205 float y=ReadFloat();
206 float z=ReadFloat();
207 return aiVector3D( x,y,z );
208}
209
210// ------------------------------------------------------------------------------------------------
211aiQuaternion 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// ------------------------------------------------------------------------------------------------
221string 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// ------------------------------------------------------------------------------------------------
233string 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// ------------------------------------------------------------------------------------------------
247void B3DImporter::ExitChunk(){
248 _pos=_stack.back();
249 _stack.pop_back();
250}
251
252// ------------------------------------------------------------------------------------------------
253unsigned B3DImporter::ChunkSize(){
254 return _stack.back()-_pos;
255}
256// ------------------------------------------------------------------------------------------------
257
258template<class T>
259T *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// ------------------------------------------------------------------------------------------------
271void 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// ------------------------------------------------------------------------------------------------
285void 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// ------------------------------------------------------------------------------------------------
340void 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// ------------------------------------------------------------------------------------------------
378void 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// ------------------------------------------------------------------------------------------------
421void 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// ------------------------------------------------------------------------------------------------
438void 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// ------------------------------------------------------------------------------------------------
464void 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// ------------------------------------------------------------------------------------------------
498void 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// ------------------------------------------------------------------------------------------------
511aiNode *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// ------------------------------------------------------------------------------------------------
574void 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