1
2/*
3Open Asset Import Library (assimp)
4----------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8All rights reserved.
9
10Redistribution and use of this software in source and binary forms,
11with or without modification, are permitted provided that the
12following 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
28THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38OF 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
76namespace Assimp {
77 template<> const char* LogFunctions<BlenderImporter>::Prefix()
78 {
79 static auto prefix = "BLEND: ";
80 return prefix;
81 }
82}
83
84using namespace Assimp;
85using namespace Assimp::Blender;
86using namespace Assimp::Formatter;
87
88static 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
104BlenderImporter::BlenderImporter()
105: modifier_cache(new BlenderModifierShowcase()) {
106 // empty
107}
108
109// ------------------------------------------------------------------------------------------------
110// Destructor, private as well
111BlenderImporter::~BlenderImporter()
112{
113 delete modifier_cache;
114}
115
116static const char* Tokens[] = { "BLENDER" };
117static const char* TokensForSearch[] = { "blender" };
118
119// ------------------------------------------------------------------------------------------------
120// Returns whether the class can handle the format of the given file.
121bool 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
137void BlenderImporter::GetExtensionList(std::set<std::string>& app)
138{
139 app.insert("blend");
140}
141
142// ------------------------------------------------------------------------------------------------
143// Loader registry entry
144const aiImporterDesc* BlenderImporter::GetInfo () const
145{
146 return &blenderDesc;
147}
148
149// ------------------------------------------------------------------------------------------------
150// Setup configuration properties for the loader
151void BlenderImporter::SetupProperties(const Importer* /*pImp*/)
152{
153 // nothing to be done for the moment
154}
155
156struct free_it {
157 free_it(void* free) : free(free) {}
158 ~free_it() {
159 ::free(this->free);
160 }
161
162 void* free;
163};
164
165// ------------------------------------------------------------------------------------------------
166// Imports the given file into the given scene structure.
167void 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// ------------------------------------------------------------------------------------------------
273void 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// ------------------------------------------------------------------------------------------------
307void 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// ------------------------------------------------------------------------------------------------
347void 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// ------------------------------------------------------------------------------------------------
427void 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// ------------------------------------------------------------------------------------------------
506void 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// ------------------------------------------------------------------------------------------------
520void 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// ------------------------------------------------------------------------------------------------
568void 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
602void 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
713void 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// ------------------------------------------------------------------------------------------------
782void 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// ------------------------------------------------------------------------------------------------
794void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type)
795{
796 LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" ));
797}
798
799// ------------------------------------------------------------------------------------------------
800void 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// ------------------------------------------------------------------------------------------------
1149aiCamera* 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// ------------------------------------------------------------------------------------------------
1166aiLight* 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// ------------------------------------------------------------------------------------------------
1210aiNode* 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