1
2 /*
3 Open Asset Import Library (assimp)
4 ----------------------------------------------------------------------
5
6 Copyright (c) 2006-2015, assimp team
7 All rights reserved.
8
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22 * Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39 ----------------------------------------------------------------------
40 */
41
42 /** @file BlenderLoader.cpp
43 * @brief Implementation of the Blender3D importer class.
44 */
45
46
47 //#define ASSIMP_BUILD_NO_COMPRESSED_BLEND
48 // Uncomment this to disable support for (gzip)compressed .BLEND files
49
50 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
51
52 #include "BlenderIntermediate.h"
53 #include "BlenderModifier.h"
54 #include "BlenderBMesh.h"
55 #include "../include/assimp/scene.h"
56 #include "StringComparison.h"
57
58 #include "StreamReader.h"
59 #include "MemoryIOWrapper.h"
60 #include <cctype>
61
62
63 // zlib is needed for compressed blend files
64 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
65 # ifdef ASSIMP_BUILD_NO_OWN_ZLIB
66 # include <zlib.h>
67 # else
68 # include "../contrib/zlib/zlib.h"
69 # endif
70 #endif
71
72 namespace Assimp {
73 template<> const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
74 }
75
76 using namespace Assimp;
77 using namespace Assimp::Blender;
78 using namespace Assimp::Formatter;
79
80 static const aiImporterDesc blenderDesc = {
81 "Blender 3D Importer \nhttp://www.blender3d.org",
82 "",
83 "",
84 "No animation support yet",
85 aiImporterFlags_SupportBinaryFlavour,
86 0,
87 0,
88 2,
89 50,
90 "blend"
91 };
92
93
94 // ------------------------------------------------------------------------------------------------
95 // Constructor to be privately used by Importer
BlenderImporter()96 BlenderImporter::BlenderImporter()
97 : modifier_cache(new BlenderModifierShowcase())
98 {}
99
100 // ------------------------------------------------------------------------------------------------
101 // Destructor, private as well
~BlenderImporter()102 BlenderImporter::~BlenderImporter()
103 {
104 delete modifier_cache;
105 }
106
107 // ------------------------------------------------------------------------------------------------
108 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const109 bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
110 {
111 const std::string& extension = GetExtension(pFile);
112 if (extension == "blend") {
113 return true;
114 }
115
116 else if ((!extension.length() || checkSig) && pIOHandler) {
117 // note: this won't catch compressed files
118 const char* tokens[] = {"BLENDER"};
119 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
120 }
121 return false;
122 }
123
124 // ------------------------------------------------------------------------------------------------
125 // List all extensions handled by this loader
GetExtensionList(std::set<std::string> & app)126 void BlenderImporter::GetExtensionList(std::set<std::string>& app)
127 {
128 app.insert("blend");
129 }
130
131 // ------------------------------------------------------------------------------------------------
132 // Loader registry entry
GetInfo() const133 const aiImporterDesc* BlenderImporter::GetInfo () const
134 {
135 return &blenderDesc;
136 }
137
138 // ------------------------------------------------------------------------------------------------
139 // Setup configuration properties for the loader
SetupProperties(const Importer *)140 void BlenderImporter::SetupProperties(const Importer* /*pImp*/)
141 {
142 // nothing to be done for the moment
143 }
144
145 struct free_it
146 {
free_itfree_it147 free_it(void* free) : free(free) {}
~free_itfree_it148 ~free_it() {
149 ::free(this->free);
150 }
151
152 void* free;
153 };
154
155 // ------------------------------------------------------------------------------------------------
156 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)157 void BlenderImporter::InternReadFile( const std::string& pFile,
158 aiScene* pScene, IOSystem* pIOHandler)
159 {
160 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
161 Bytef* dest = NULL;
162 free_it free_it_really(dest);
163 #endif
164
165 FileDatabase file;
166 boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
167 if (!stream) {
168 ThrowException("Could not open file for reading");
169 }
170
171 char magic[8] = {0};
172 stream->Read(magic,7,1);
173 if (strcmp(magic,"BLENDER")) {
174 // Check for presence of the gzip header. If yes, assume it is a
175 // compressed blend file and try uncompressing it, else fail. This is to
176 // avoid uncompressing random files which our loader might end up with.
177 #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
178 ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
179 #else
180
181 if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
182 ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
183 }
184
185 LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
186 if (magic[2] != 8) {
187 ThrowException("Unsupported GZIP compression method");
188 }
189
190 // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
191 stream->Seek(0L,aiOrigin_SET);
192 boost::shared_ptr<StreamReaderLE> reader = boost::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
193
194 // build a zlib stream
195 z_stream zstream;
196 zstream.opaque = Z_NULL;
197 zstream.zalloc = Z_NULL;
198 zstream.zfree = Z_NULL;
199 zstream.data_type = Z_BINARY;
200
201 // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
202 inflateInit2(&zstream, 16+MAX_WBITS);
203
204 zstream.next_in = reinterpret_cast<Bytef*>( reader->GetPtr() );
205 zstream.avail_in = reader->GetRemainingSize();
206
207 size_t total = 0l;
208
209 // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
210 #define MYBLOCK 1024
211 Bytef block[MYBLOCK];
212 int ret;
213 do {
214 zstream.avail_out = MYBLOCK;
215 zstream.next_out = block;
216 ret = inflate(&zstream, Z_NO_FLUSH);
217
218 if (ret != Z_STREAM_END && ret != Z_OK) {
219 ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
220 }
221 const size_t have = MYBLOCK - zstream.avail_out;
222 total += have;
223 dest = reinterpret_cast<Bytef*>( realloc(dest,total) );
224 memcpy(dest + total - have,block,have);
225 }
226 while (ret != Z_STREAM_END);
227
228 // terminate zlib
229 inflateEnd(&zstream);
230
231 // replace the input stream with a memory stream
232 stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(dest),total));
233
234 // .. and retry
235 stream->Read(magic,7,1);
236 if (strcmp(magic,"BLENDER")) {
237 ThrowException("Found no BLENDER magic word in decompressed GZIP file");
238 }
239 #endif
240 }
241
242 file.i64bit = (stream->Read(magic,1,1),magic[0]=='-');
243 file.little = (stream->Read(magic,1,1),magic[0]=='v');
244
245 stream->Read(magic,3,1);
246 magic[3] = '\0';
247
248 LogInfo((format(),"Blender version is ",magic[0],".",magic+1,
249 " (64bit: ",file.i64bit?"true":"false",
250 ", little endian: ",file.little?"true":"false",")"
251 ));
252
253 ParseBlendFile(file,stream);
254
255 Scene scene;
256 ExtractScene(scene,file);
257
258 ConvertBlendFile(pScene,scene,file);
259 }
260
261 // ------------------------------------------------------------------------------------------------
ParseBlendFile(FileDatabase & out,boost::shared_ptr<IOStream> stream)262 void BlenderImporter::ParseBlendFile(FileDatabase& out, boost::shared_ptr<IOStream> stream)
263 {
264 out.reader = boost::shared_ptr<StreamReaderAny>(new StreamReaderAny(stream,out.little));
265
266 DNAParser dna_reader(out);
267 const DNA* dna = NULL;
268
269 out.entries.reserve(128); { // even small BLEND files tend to consist of many file blocks
270 SectionParser parser(*out.reader.get(),out.i64bit);
271
272 // first parse the file in search for the DNA and insert all other sections into the database
273 while ((parser.Next(),1)) {
274 const FileBlockHead& head = parser.GetCurrent();
275
276 if (head.id == "ENDB") {
277 break; // only valid end of the file
278 }
279 else if (head.id == "DNA1") {
280 dna_reader.Parse();
281 dna = &dna_reader.GetDNA();
282 continue;
283 }
284
285 out.entries.push_back(head);
286 }
287 }
288 if (!dna) {
289 ThrowException("SDNA not found");
290 }
291
292 std::sort(out.entries.begin(),out.entries.end());
293 }
294
295 // ------------------------------------------------------------------------------------------------
ExtractScene(Scene & out,const FileDatabase & file)296 void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
297 {
298 const FileBlockHead* block = NULL;
299 std::map<std::string,size_t>::const_iterator it = file.dna.indices.find("Scene");
300 if (it == file.dna.indices.end()) {
301 ThrowException("There is no `Scene` structure record");
302 }
303
304 const Structure& ss = file.dna.structures[(*it).second];
305
306 // we need a scene somewhere to start with.
307 for_each(const FileBlockHead& bl,file.entries) {
308
309 // Fix: using the DNA index is more reliable to locate scenes
310 //if (bl.id == "SC") {
311
312 if (bl.dna_index == (*it).second) {
313 block = &bl;
314 break;
315 }
316 }
317
318 if (!block) {
319 ThrowException("There is not a single `Scene` record to load");
320 }
321
322 file.reader->SetCurrentPos(block->start);
323 ss.Convert(out,file);
324
325 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
326 DefaultLogger::get()->info((format(),
327 "(Stats) Fields read: " ,file.stats().fields_read,
328 ", pointers resolved: " ,file.stats().pointers_resolved,
329 ", cache hits: " ,file.stats().cache_hits,
330 ", cached objects: " ,file.stats().cached_objects
331 ));
332 #endif
333 }
334
335 // ------------------------------------------------------------------------------------------------
ConvertBlendFile(aiScene * out,const Scene & in,const FileDatabase & file)336 void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file)
337 {
338 ConversionData conv(file);
339
340 // FIXME it must be possible to take the hierarchy directly from
341 // the file. This is terrible. Here, we're first looking for
342 // all objects which don't have parent objects at all -
343 std::deque<const Object*> no_parents;
344 for (boost::shared_ptr<Base> cur = boost::static_pointer_cast<Base> ( in.base.first ); cur; cur = cur->next) {
345 if (cur->object) {
346 if(!cur->object->parent) {
347 no_parents.push_back(cur->object.get());
348 }
349 else conv.objects.insert(cur->object.get());
350 }
351 }
352 for (boost::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
353 if (cur->object) {
354 if(cur->object->parent) {
355 conv.objects.insert(cur->object.get());
356 }
357 }
358 }
359
360 if (no_parents.empty()) {
361 ThrowException("Expected at least one object with no parent");
362 }
363
364 aiNode* root = out->mRootNode = new aiNode("<BlenderRoot>");
365
366 root->mNumChildren = static_cast<unsigned int>(no_parents.size());
367 root->mChildren = new aiNode*[root->mNumChildren]();
368 for (unsigned int i = 0; i < root->mNumChildren; ++i) {
369 root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4());
370 root->mChildren[i]->mParent = root;
371 }
372
373 BuildMaterials(conv);
374
375 if (conv.meshes->size()) {
376 out->mMeshes = new aiMesh*[out->mNumMeshes = static_cast<unsigned int>( conv.meshes->size() )];
377 std::copy(conv.meshes->begin(),conv.meshes->end(),out->mMeshes);
378 conv.meshes.dismiss();
379 }
380
381 if (conv.lights->size()) {
382 out->mLights = new aiLight*[out->mNumLights = static_cast<unsigned int>( conv.lights->size() )];
383 std::copy(conv.lights->begin(),conv.lights->end(),out->mLights);
384 conv.lights.dismiss();
385 }
386
387 if (conv.cameras->size()) {
388 out->mCameras = new aiCamera*[out->mNumCameras = static_cast<unsigned int>( conv.cameras->size() )];
389 std::copy(conv.cameras->begin(),conv.cameras->end(),out->mCameras);
390 conv.cameras.dismiss();
391 }
392
393 if (conv.materials->size()) {
394 out->mMaterials = new aiMaterial*[out->mNumMaterials = static_cast<unsigned int>( conv.materials->size() )];
395 std::copy(conv.materials->begin(),conv.materials->end(),out->mMaterials);
396 conv.materials.dismiss();
397 }
398
399 if (conv.textures->size()) {
400 out->mTextures = new aiTexture*[out->mNumTextures = static_cast<unsigned int>( conv.textures->size() )];
401 std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures);
402 conv.textures.dismiss();
403 }
404
405 // acknowledge that the scene might come out incomplete
406 // by Assimps definition of `complete`: blender scenes
407 // can consist of thousands of cameras or lights with
408 // not a single mesh between them.
409 if (!out->mNumMeshes) {
410 out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
411 }
412 }
413
414 // ------------------------------------------------------------------------------------------------
ResolveImage(aiMaterial * out,const Material * mat,const MTex * tex,const Image * img,ConversionData & conv_data)415 void BlenderImporter::ResolveImage(aiMaterial* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data)
416 {
417 (void)mat; (void)tex; (void)conv_data;
418 aiString name;
419
420 // check if the file contents are bundled with the BLEND file
421 if (img->packedfile) {
422 name.data[0] = '*';
423 name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size());
424
425 conv_data.textures->push_back(new aiTexture());
426 aiTexture* tex = conv_data.textures->back();
427
428 // usually 'img->name' will be the original file name of the embedded textures,
429 // so we can extract the file extension from it.
430 const size_t nlen = strlen( img->name );
431 const char* s = img->name+nlen, *e = s;
432
433 while (s >= img->name && *s != '.')--s;
434
435 tex->achFormatHint[0] = s+1>e ? '\0' : ::tolower( s[1] );
436 tex->achFormatHint[1] = s+2>e ? '\0' : ::tolower( s[2] );
437 tex->achFormatHint[2] = s+3>e ? '\0' : ::tolower( s[3] );
438 tex->achFormatHint[3] = '\0';
439
440 // tex->mHeight = 0;
441 tex->mWidth = img->packedfile->size;
442 uint8_t* ch = new uint8_t[tex->mWidth];
443
444 conv_data.db.reader->SetCurrentPos(static_cast<size_t>( img->packedfile->data->val));
445 conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
446
447 tex->pcData = reinterpret_cast<aiTexel*>(ch);
448
449 LogInfo("Reading embedded texture, original file was "+std::string(img->name));
450 }
451 else {
452 name = aiString( img->name );
453 }
454
455 aiTextureType texture_type = aiTextureType_UNKNOWN;
456 MTex::MapType map_type = tex->mapto;
457
458 if (map_type & MTex::MapType_COL)
459 texture_type = aiTextureType_DIFFUSE;
460 else if (map_type & MTex::MapType_NORM) {
461 if (tex->tex->imaflag & Tex::ImageFlags_NORMALMAP) {
462 texture_type = aiTextureType_NORMALS;
463 }
464 else {
465 texture_type = aiTextureType_HEIGHT;
466 }
467 out->AddProperty(&tex->norfac,1,AI_MATKEY_BUMPSCALING);
468 }
469 else if (map_type & MTex::MapType_COLSPEC)
470 texture_type = aiTextureType_SPECULAR;
471 else if (map_type & MTex::MapType_COLMIR)
472 texture_type = aiTextureType_REFLECTION;
473 //else if (map_type & MTex::MapType_REF)
474 else if (map_type & MTex::MapType_SPEC)
475 texture_type = aiTextureType_SHININESS;
476 else if (map_type & MTex::MapType_EMIT)
477 texture_type = aiTextureType_EMISSIVE;
478 //else if (map_type & MTex::MapType_ALPHA)
479 //else if (map_type & MTex::MapType_HAR)
480 //else if (map_type & MTex::MapType_RAYMIRR)
481 //else if (map_type & MTex::MapType_TRANSLU)
482 else if (map_type & MTex::MapType_AMB)
483 texture_type = aiTextureType_AMBIENT;
484 else if (map_type & MTex::MapType_DISPLACE)
485 texture_type = aiTextureType_DISPLACEMENT;
486 //else if (map_type & MTex::MapType_WARP)
487
488 out->AddProperty(&name,AI_MATKEY_TEXTURE(texture_type,
489 conv_data.next_texture[texture_type]++));
490
491 }
492
493 // ------------------------------------------------------------------------------------------------
AddSentinelTexture(aiMaterial * out,const Material * mat,const MTex * tex,ConversionData & conv_data)494 void BlenderImporter::AddSentinelTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
495 {
496 (void)mat; (void)tex; (void)conv_data;
497
498 aiString name;
499 name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++,
500 GetTextureTypeDisplayString(tex->tex->type)
501 );
502 out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
503 conv_data.next_texture[aiTextureType_DIFFUSE]++)
504 );
505 }
506
507 // ------------------------------------------------------------------------------------------------
ResolveTexture(aiMaterial * out,const Material * mat,const MTex * tex,ConversionData & conv_data)508 void BlenderImporter::ResolveTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
509 {
510 const Tex* rtex = tex->tex.get();
511 if(!rtex || !rtex->type) {
512 return;
513 }
514
515 // We can't support most of the texture types because they're mostly procedural.
516 // These are substituted by a dummy texture.
517 const char* dispnam = "";
518 switch( rtex->type )
519 {
520 // these are listed in blender's UI
521 case Tex::Type_CLOUDS :
522 case Tex::Type_WOOD :
523 case Tex::Type_MARBLE :
524 case Tex::Type_MAGIC :
525 case Tex::Type_BLEND :
526 case Tex::Type_STUCCI :
527 case Tex::Type_NOISE :
528 case Tex::Type_PLUGIN :
529 case Tex::Type_MUSGRAVE :
530 case Tex::Type_VORONOI :
531 case Tex::Type_DISTNOISE :
532 case Tex::Type_ENVMAP :
533
534 // these do no appear in the UI, why?
535 case Tex::Type_POINTDENSITY :
536 case Tex::Type_VOXELDATA :
537
538 LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam);
539 AddSentinelTexture(out, mat, tex, conv_data);
540 break;
541
542 case Tex::Type_IMAGE :
543 if (!rtex->ima) {
544 LogError("A texture claims to be an Image, but no image reference is given");
545 break;
546 }
547 ResolveImage(out, mat, tex, rtex->ima.get(),conv_data);
548 break;
549
550 default:
551 ai_assert(false);
552 };
553 }
554
555 // ------------------------------------------------------------------------------------------------
BuildMaterials(ConversionData & conv_data)556 void BlenderImporter::BuildMaterials(ConversionData& conv_data)
557 {
558 conv_data.materials->reserve(conv_data.materials_raw.size());
559
560 // add a default material if necessary
561 unsigned int index = static_cast<unsigned int>( -1 );
562 for_each( aiMesh* mesh, conv_data.meshes.get() ) {
563 if (mesh->mMaterialIndex == static_cast<unsigned int>( -1 )) {
564
565 if (index == static_cast<unsigned int>( -1 )) {
566 // Setup a default material.
567 boost::shared_ptr<Material> p(new Material());
568 ai_assert(::strlen(AI_DEFAULT_MATERIAL_NAME) < sizeof(p->id.name)-2);
569 strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME );
570
571 // Note: MSVC11 does not zero-initialize Material here, although it should.
572 // Thus all relevant fields should be explicitly initialized. We cannot add
573 // a default constructor to Material since the DNA codegen does not support
574 // parsing it.
575 p->r = p->g = p->b = 0.6f;
576 p->specr = p->specg = p->specb = 0.6f;
577 p->ambr = p->ambg = p->ambb = 0.0f;
578 p->mirr = p->mirg = p->mirb = 0.0f;
579 p->emit = 0.f;
580 p->alpha = 0.f;
581 p->har = 0;
582
583 index = static_cast<unsigned int>( conv_data.materials_raw.size() );
584 conv_data.materials_raw.push_back(p);
585 LogInfo("Adding default material");
586 }
587 mesh->mMaterialIndex = index;
588 }
589 }
590
591 for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) {
592
593 // reset per material global counters
594 for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) {
595 conv_data.next_texture[i] = 0 ;
596 }
597
598 aiMaterial* mout = new aiMaterial();
599 conv_data.materials->push_back(mout);
600 // For any new material field handled here, the default material above must be updated with an appropriate default value.
601
602 // set material name
603 aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA'
604 mout->AddProperty(&name,AI_MATKEY_NAME);
605
606
607 // basic material colors
608 aiColor3D col(mat->r,mat->g,mat->b);
609 if (mat->r || mat->g || mat->b ) {
610
611 // Usually, zero diffuse color means no diffuse color at all in the equation.
612 // So we omit this member to express this intent.
613 mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE);
614
615 if (mat->emit) {
616 aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b) ;
617 mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE) ;
618 }
619 }
620
621 col = aiColor3D(mat->specr,mat->specg,mat->specb);
622 mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR);
623
624 // is hardness/shininess set?
625 if( mat->har ) {
626 const float har = mat->har;
627 mout->AddProperty(&har,1,AI_MATKEY_SHININESS);
628 }
629
630 col = aiColor3D(mat->ambr,mat->ambg,mat->ambb);
631 mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT);
632
633 col = aiColor3D(mat->mirr,mat->mirg,mat->mirb);
634 mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE);
635
636 for(size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
637 if (!mat->mtex[i]) {
638 continue;
639 }
640
641 ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data);
642 }
643 }
644 }
645
646 // ------------------------------------------------------------------------------------------------
CheckActualType(const ElemBase * dt,const char * check)647 void BlenderImporter::CheckActualType(const ElemBase* dt, const char* check)
648 {
649 ai_assert(dt);
650 if (strcmp(dt->dna_type,check)) {
651 ThrowException((format(),
652 "Expected object at ",std::hex,dt," to be of type `",check,
653 "`, but it claims to be a `",dt->dna_type,"`instead"
654 ));
655 }
656 }
657
658 // ------------------------------------------------------------------------------------------------
NotSupportedObjectType(const Object * obj,const char * type)659 void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type)
660 {
661 LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" ));
662 }
663
664 // ------------------------------------------------------------------------------------------------
ConvertMesh(const Scene &,const Object *,const Mesh * mesh,ConversionData & conv_data,TempArray<std::vector,aiMesh> & temp)665 void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, const Mesh* mesh,
666 ConversionData& conv_data, TempArray<std::vector,aiMesh>& temp
667 )
668 {
669 // TODO: Resolve various problems with BMesh triangluation before re-enabling.
670 // See issues #400, #373, #318 #315 and #132.
671 #if defined(TODO_FIX_BMESH_CONVERSION)
672 BlenderBMeshConverter BMeshConverter( mesh );
673 if ( BMeshConverter.ContainsBMesh( ) )
674 {
675 mesh = BMeshConverter.TriangulateBMesh( );
676 }
677 #endif
678
679 typedef std::pair<const int,size_t> MyPair;
680 if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) {
681 return;
682 }
683
684 // some sanity checks
685 if (static_cast<size_t> ( mesh->totface ) > mesh->mface.size() ){
686 ThrowException("Number of faces is larger than the corresponding array");
687 }
688
689 if (static_cast<size_t> ( mesh->totvert ) > mesh->mvert.size()) {
690 ThrowException("Number of vertices is larger than the corresponding array");
691 }
692
693 if (static_cast<size_t> ( mesh->totloop ) > mesh->mloop.size()) {
694 ThrowException("Number of vertices is larger than the corresponding array");
695 }
696
697 // collect per-submesh numbers
698 std::map<int,size_t> per_mat;
699 std::map<int,size_t> per_mat_verts;
700 for (int i = 0; i < mesh->totface; ++i) {
701
702 const MFace& mf = mesh->mface[i];
703 per_mat[ mf.mat_nr ]++;
704 per_mat_verts[ mf.mat_nr ] += mf.v4?4:3;
705 }
706
707 for (int i = 0; i < mesh->totpoly; ++i) {
708 const MPoly& mp = mesh->mpoly[i];
709 per_mat[ mp.mat_nr ]++;
710 per_mat_verts[ mp.mat_nr ] += mp.totloop;
711 }
712
713 // ... and allocate the corresponding meshes
714 const size_t old = temp->size();
715 temp->reserve(temp->size() + per_mat.size());
716
717 std::map<size_t,size_t> mat_num_to_mesh_idx;
718 for_each(MyPair& it, per_mat) {
719
720 mat_num_to_mesh_idx[it.first] = temp->size();
721 temp->push_back(new aiMesh());
722
723 aiMesh* out = temp->back();
724 out->mVertices = new aiVector3D[per_mat_verts[it.first]];
725 out->mNormals = new aiVector3D[per_mat_verts[it.first]];
726
727 //out->mNumFaces = 0
728 //out->mNumVertices = 0
729 out->mFaces = new aiFace[it.second]();
730
731 // all submeshes created from this mesh are named equally. this allows
732 // curious users to recover the original adjacency.
733 out->mName = aiString(mesh->id.name+2);
734 // skip over the name prefix 'ME'
735
736 // resolve the material reference and add this material to the set of
737 // output materials. The (temporary) material index is the index
738 // of the material entry within the list of resolved materials.
739 if (mesh->mat) {
740
741 if (static_cast<size_t> ( it.first ) >= mesh->mat.size() ) {
742 ThrowException("Material index is out of range");
743 }
744
745 boost::shared_ptr<Material> mat = mesh->mat[it.first];
746 const std::deque< boost::shared_ptr<Material> >::iterator has = std::find(
747 conv_data.materials_raw.begin(),
748 conv_data.materials_raw.end(),mat
749 );
750
751 if (has != conv_data.materials_raw.end()) {
752 out->mMaterialIndex = static_cast<unsigned int>( std::distance(conv_data.materials_raw.begin(),has));
753 }
754 else {
755 out->mMaterialIndex = static_cast<unsigned int>( conv_data.materials_raw.size() );
756 conv_data.materials_raw.push_back(mat);
757 }
758 }
759 else out->mMaterialIndex = static_cast<unsigned int>( -1 );
760 }
761
762 for (int i = 0; i < mesh->totface; ++i) {
763
764 const MFace& mf = mesh->mface[i];
765
766 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
767 aiFace& f = out->mFaces[out->mNumFaces++];
768
769 f.mIndices = new unsigned int[ f.mNumIndices = mf.v4?4:3 ];
770 aiVector3D* vo = out->mVertices + out->mNumVertices;
771 aiVector3D* vn = out->mNormals + out->mNumVertices;
772
773 // XXX we can't fold this easily, because we are restricted
774 // to the member names from the BLEND file (v1,v2,v3,v4)
775 // which are assigned by the genblenddna.py script and
776 // cannot be changed without breaking the entire
777 // import process.
778
779 if (mf.v1 >= mesh->totvert) {
780 ThrowException("Vertex index v1 out of range");
781 }
782 const MVert* v = &mesh->mvert[mf.v1];
783 vo->x = v->co[0];
784 vo->y = v->co[1];
785 vo->z = v->co[2];
786 vn->x = v->no[0];
787 vn->y = v->no[1];
788 vn->z = v->no[2];
789 f.mIndices[0] = out->mNumVertices++;
790 ++vo;
791 ++vn;
792
793 // if (f.mNumIndices >= 2) {
794 if (mf.v2 >= mesh->totvert) {
795 ThrowException("Vertex index v2 out of range");
796 }
797 v = &mesh->mvert[mf.v2];
798 vo->x = v->co[0];
799 vo->y = v->co[1];
800 vo->z = v->co[2];
801 vn->x = v->no[0];
802 vn->y = v->no[1];
803 vn->z = v->no[2];
804 f.mIndices[1] = out->mNumVertices++;
805 ++vo;
806 ++vn;
807
808 if (mf.v3 >= mesh->totvert) {
809 ThrowException("Vertex index v3 out of range");
810 }
811 // if (f.mNumIndices >= 3) {
812 v = &mesh->mvert[mf.v3];
813 vo->x = v->co[0];
814 vo->y = v->co[1];
815 vo->z = v->co[2];
816 vn->x = v->no[0];
817 vn->y = v->no[1];
818 vn->z = v->no[2];
819 f.mIndices[2] = out->mNumVertices++;
820 ++vo;
821 ++vn;
822
823 if (mf.v4 >= mesh->totvert) {
824 ThrowException("Vertex index v4 out of range");
825 }
826 // if (f.mNumIndices >= 4) {
827 if (mf.v4) {
828 v = &mesh->mvert[mf.v4];
829 vo->x = v->co[0];
830 vo->y = v->co[1];
831 vo->z = v->co[2];
832 vn->x = v->no[0];
833 vn->y = v->no[1];
834 vn->z = v->no[2];
835 f.mIndices[3] = out->mNumVertices++;
836 ++vo;
837 ++vn;
838
839 out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
840 }
841 else out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
842
843 // }
844 // }
845 // }
846 }
847
848 for (int i = 0; i < mesh->totpoly; ++i) {
849
850 const MPoly& mf = mesh->mpoly[i];
851
852 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
853 aiFace& f = out->mFaces[out->mNumFaces++];
854
855 f.mIndices = new unsigned int[ f.mNumIndices = mf.totloop ];
856 aiVector3D* vo = out->mVertices + out->mNumVertices;
857 aiVector3D* vn = out->mNormals + out->mNumVertices;
858
859 // XXX we can't fold this easily, because we are restricted
860 // to the member names from the BLEND file (v1,v2,v3,v4)
861 // which are assigned by the genblenddna.py script and
862 // cannot be changed without breaking the entire
863 // import process.
864 for (int j = 0;j < mf.totloop; ++j)
865 {
866 const MLoop& loop = mesh->mloop[mf.loopstart + j];
867
868 if (loop.v >= mesh->totvert) {
869 ThrowException("Vertex index out of range");
870 }
871
872 const MVert& v = mesh->mvert[loop.v];
873
874 vo->x = v.co[0];
875 vo->y = v.co[1];
876 vo->z = v.co[2];
877 vn->x = v.no[0];
878 vn->y = v.no[1];
879 vn->z = v.no[2];
880 f.mIndices[j] = out->mNumVertices++;
881
882 ++vo;
883 ++vn;
884
885 }
886 if (mf.totloop == 3)
887 {
888 out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
889 }
890 else
891 {
892 out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
893 }
894 }
895
896 // collect texture coordinates, they're stored in a separate per-face buffer
897 if (mesh->mtface || mesh->mloopuv) {
898 if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
899 ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)");
900 }
901 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
902 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
903
904 (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
905 (*it)->mNumFaces = (*it)->mNumVertices = 0;
906 }
907
908 for (int i = 0; i < mesh->totface; ++i) {
909 const MTFace* v = &mesh->mtface[i];
910
911 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
912 const aiFace& f = out->mFaces[out->mNumFaces++];
913
914 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
915 for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
916 vo->x = v->uv[i][0];
917 vo->y = v->uv[i][1];
918 }
919 }
920
921 for (int i = 0; i < mesh->totpoly; ++i) {
922 const MPoly& v = mesh->mpoly[i];
923 aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
924 const aiFace& f = out->mFaces[out->mNumFaces++];
925
926 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
927 for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
928 const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
929 vo->x = uv.uv[0];
930 vo->y = uv.uv[1];
931 }
932
933 }
934 }
935
936 // collect texture coordinates, old-style (marked as deprecated in current blender sources)
937 if (mesh->tface) {
938 if (mesh->totface > static_cast<int> ( mesh->tface.size())) {
939 ThrowException("Number of faces is larger than the corresponding UV face array (#2)");
940 }
941 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
942 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
943
944 (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
945 (*it)->mNumFaces = (*it)->mNumVertices = 0;
946 }
947
948 for (int i = 0; i < mesh->totface; ++i) {
949 const TFace* v = &mesh->tface[i];
950
951 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
952 const aiFace& f = out->mFaces[out->mNumFaces++];
953
954 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
955 for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
956 vo->x = v->uv[i][0];
957 vo->y = v->uv[i][1];
958 }
959 }
960 }
961
962 // collect vertex colors, stored separately as well
963 if (mesh->mcol || mesh->mloopcol) {
964 if (mesh->totface > static_cast<int> ( (mesh->mcol.size()/4)) ) {
965 ThrowException("Number of faces is larger than the corresponding color face array");
966 }
967 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
968 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
969
970 (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices];
971 (*it)->mNumFaces = (*it)->mNumVertices = 0;
972 }
973
974 for (int i = 0; i < mesh->totface; ++i) {
975
976 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
977 const aiFace& f = out->mFaces[out->mNumFaces++];
978
979 aiColor4D* vo = &out->mColors[0][out->mNumVertices];
980 for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo,++out->mNumVertices) {
981 const MCol* col = &mesh->mcol[(i<<2)+n];
982
983 vo->r = col->r;
984 vo->g = col->g;
985 vo->b = col->b;
986 vo->a = col->a;
987 }
988 for (unsigned int n = f.mNumIndices; n < 4; ++n);
989 }
990
991 for (int i = 0; i < mesh->totpoly; ++i) {
992 const MPoly& v = mesh->mpoly[i];
993 aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
994 const aiFace& f = out->mFaces[out->mNumFaces++];
995
996 aiColor4D* vo = &out->mColors[0][out->mNumVertices];
997 for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
998 const MLoopCol& col = mesh->mloopcol[v.loopstart + j];
999 vo->r = col.r;
1000 vo->g = col.g;
1001 vo->b = col.b;
1002 vo->a = col.a;
1003 }
1004
1005 }
1006
1007 }
1008
1009 return;
1010 }
1011
1012 // ------------------------------------------------------------------------------------------------
ConvertCamera(const Scene &,const Object * obj,const Camera *,ConversionData &)1013 aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, const Camera* /*camera*/, ConversionData& /*conv_data*/)
1014 {
1015 ScopeGuard<aiCamera> out(new aiCamera());
1016 out->mName = obj->id.name+2;
1017 out->mPosition = aiVector3D(0.f, 0.f, 0.f);
1018 out->mUp = aiVector3D(0.f, 1.f, 0.f);
1019 out->mLookAt = aiVector3D(0.f, 0.f, -1.f);
1020 return out.dismiss();
1021 }
1022
1023 // ------------------------------------------------------------------------------------------------
ConvertLight(const Scene &,const Object * obj,const Lamp * lamp,ConversionData &)1024 aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, const Lamp* lamp, ConversionData& /*conv_data*/)
1025 {
1026 ScopeGuard<aiLight> out(new aiLight());
1027 out->mName = obj->id.name+2;
1028
1029 switch (lamp->type)
1030 {
1031 case Lamp::Type_Local:
1032 out->mType = aiLightSource_POINT;
1033 break;
1034 case Lamp::Type_Sun:
1035 out->mType = aiLightSource_DIRECTIONAL;
1036
1037 // blender orients directional lights as facing toward -z
1038 out->mDirection = aiVector3D(0.f, 0.f, -1.f);
1039 break;
1040 default:
1041 break;
1042 }
1043
1044 out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
1045 out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
1046 out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
1047 return out.dismiss();
1048 }
1049
1050 // ------------------------------------------------------------------------------------------------
ConvertNode(const Scene & in,const Object * obj,ConversionData & conv_data,const aiMatrix4x4 & parentTransform)1051 aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform)
1052 {
1053 std::deque<const Object*> children;
1054 for(ObjectSet::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
1055 const Object* object = *it;
1056 if (object->parent == obj) {
1057 children.push_back(object);
1058
1059 conv_data.objects.erase(it++);
1060 continue;
1061 }
1062 ++it;
1063 }
1064
1065 ScopeGuard<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
1066 if (obj->data) {
1067 switch (obj->type)
1068 {
1069 case Object :: Type_EMPTY:
1070 break; // do nothing
1071
1072
1073 // supported object types
1074 case Object :: Type_MESH: {
1075 const size_t old = conv_data.meshes->size();
1076
1077 CheckActualType(obj->data.get(),"Mesh");
1078 ConvertMesh(in,obj,static_cast<const Mesh*>(obj->data.get()),conv_data,conv_data.meshes);
1079
1080 if (conv_data.meshes->size() > old) {
1081 node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size()-old)];
1082 for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
1083 node->mMeshes[i] = i + old;
1084 }
1085 }}
1086 break;
1087 case Object :: Type_LAMP: {
1088 CheckActualType(obj->data.get(),"Lamp");
1089 aiLight* mesh = ConvertLight(in,obj,static_cast<const Lamp*>(
1090 obj->data.get()),conv_data);
1091
1092 if (mesh) {
1093 conv_data.lights->push_back(mesh);
1094 }}
1095 break;
1096 case Object :: Type_CAMERA: {
1097 CheckActualType(obj->data.get(),"Camera");
1098 aiCamera* mesh = ConvertCamera(in,obj,static_cast<const Camera*>(
1099 obj->data.get()),conv_data);
1100
1101 if (mesh) {
1102 conv_data.cameras->push_back(mesh);
1103 }}
1104 break;
1105
1106
1107 // unsupported object types / log, but do not break
1108 case Object :: Type_CURVE:
1109 NotSupportedObjectType(obj,"Curve");
1110 break;
1111 case Object :: Type_SURF:
1112 NotSupportedObjectType(obj,"Surface");
1113 break;
1114 case Object :: Type_FONT:
1115 NotSupportedObjectType(obj,"Font");
1116 break;
1117 case Object :: Type_MBALL:
1118 NotSupportedObjectType(obj,"MetaBall");
1119 break;
1120 case Object :: Type_WAVE:
1121 NotSupportedObjectType(obj,"Wave");
1122 break;
1123 case Object :: Type_LATTICE:
1124 NotSupportedObjectType(obj,"Lattice");
1125 break;
1126
1127 // invalid or unknown type
1128 default:
1129 break;
1130 }
1131 }
1132
1133 for(unsigned int x = 0; x < 4; ++x) {
1134 for(unsigned int y = 0; y < 4; ++y) {
1135 node->mTransformation[y][x] = obj->obmat[x][y];
1136 }
1137 }
1138
1139 aiMatrix4x4 m = parentTransform;
1140 m = m.Inverse();
1141
1142 node->mTransformation = m*node->mTransformation;
1143
1144 if (children.size()) {
1145 node->mNumChildren = static_cast<unsigned int>(children.size());
1146 aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren]();
1147 for_each (const Object* nobj,children) {
1148 *nd = ConvertNode(in,nobj,conv_data,node->mTransformation * parentTransform);
1149 (*nd++)->mParent = node;
1150 }
1151 }
1152
1153 // apply modifiers
1154 modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
1155
1156 return node.dismiss();
1157 }
1158
1159
1160 #endif
1161