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