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