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