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