vrshoot
view libs/assimp/BlenderLoader.cpp @ 2:334d17aed7de
visual studio project files
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Feb 2014 18:36:38 +0200 |
parents | |
children |
line source
2 /*
3 Open Asset Import Library (assimp)
4 ----------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
7 All rights reserved.
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:
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
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.
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.
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.
39 ----------------------------------------------------------------------
40 */
42 /** @file BlenderLoader.cpp
43 * @brief Implementation of the Blender3D importer class.
44 */
45 #include "AssimpPCH.h"
47 //#define ASSIMP_BUILD_NO_COMPRESSED_BLEND
48 // Uncomment this to disable support for (gzip)compressed .BLEND files
50 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
52 #include "BlenderIntermediate.h"
53 #include "BlenderModifier.h"
55 #include "StreamReader.h"
56 #include "MemoryIOWrapper.h"
58 // zlib is needed for compressed blend files
59 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
60 # ifdef ASSIMP_BUILD_NO_OWN_ZLIB
61 # include <zlib.h>
62 # else
63 # include "../contrib/zlib/zlib.h"
64 # endif
65 #endif
67 namespace Assimp {
68 template<> const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
69 }
71 using namespace Assimp;
72 using namespace Assimp::Blender;
73 using namespace Assimp::Formatter;
75 static const aiImporterDesc blenderDesc = {
76 "Blender 3D Importer \nhttp://www.blender3d.org",
77 "",
78 "",
79 "No animation support yet",
80 aiImporterFlags_SupportBinaryFlavour,
81 0,
82 0,
83 2,
84 50,
85 "blend"
86 };
89 // ------------------------------------------------------------------------------------------------
90 // Constructor to be privately used by Importer
91 BlenderImporter::BlenderImporter()
92 : modifier_cache(new BlenderModifierShowcase())
93 {}
95 // ------------------------------------------------------------------------------------------------
96 // Destructor, private as well
97 BlenderImporter::~BlenderImporter()
98 {
99 delete modifier_cache;
100 }
102 // ------------------------------------------------------------------------------------------------
103 // Returns whether the class can handle the format of the given file.
104 bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
105 {
106 const std::string& extension = GetExtension(pFile);
107 if (extension == "blend") {
108 return true;
109 }
111 else if ((!extension.length() || checkSig) && pIOHandler) {
112 // note: this won't catch compressed files
113 const char* tokens[] = {"BLENDER"};
114 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
115 }
116 return false;
117 }
119 // ------------------------------------------------------------------------------------------------
120 // List all extensions handled by this loader
121 void BlenderImporter::GetExtensionList(std::set<std::string>& app)
122 {
123 app.insert("blend");
124 }
126 // ------------------------------------------------------------------------------------------------
127 // Loader registry entry
128 const aiImporterDesc* BlenderImporter::GetInfo () const
129 {
130 return &blenderDesc;
131 }
133 // ------------------------------------------------------------------------------------------------
134 // Setup configuration properties for the loader
135 void BlenderImporter::SetupProperties(const Importer* /*pImp*/)
136 {
137 // nothing to be done for the moment
138 }
140 struct free_it
141 {
142 free_it(void* free) : free(free) {}
143 ~free_it() {
144 ::free(this->free);
145 }
147 void* free;
148 };
150 // ------------------------------------------------------------------------------------------------
151 // Imports the given file into the given scene structure.
152 void BlenderImporter::InternReadFile( const std::string& pFile,
153 aiScene* pScene, IOSystem* pIOHandler)
154 {
155 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
156 Bytef* dest = NULL;
157 free_it free_it_really(dest);
158 #endif
160 FileDatabase file;
161 boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
162 if (!stream) {
163 ThrowException("Could not open file for reading");
164 }
166 char magic[8] = {0};
167 stream->Read(magic,7,1);
168 if (strcmp(magic,"BLENDER")) {
169 // Check for presence of the gzip header. If yes, assume it is a
170 // compressed blend file and try uncompressing it, else fail. This is to
171 // avoid uncompressing random files which our loader might end up with.
172 #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
173 ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
174 #else
176 if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
177 ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
178 }
180 LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
181 if (magic[2] != 8) {
182 ThrowException("Unsupported GZIP compression method");
183 }
185 // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
186 stream->Seek(0L,aiOrigin_SET);
187 boost::shared_ptr<StreamReaderLE> reader = boost::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
189 // build a zlib stream
190 z_stream zstream;
191 zstream.opaque = Z_NULL;
192 zstream.zalloc = Z_NULL;
193 zstream.zfree = Z_NULL;
194 zstream.data_type = Z_BINARY;
196 // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
197 inflateInit2(&zstream, 16+MAX_WBITS);
199 zstream.next_in = reinterpret_cast<Bytef*>( reader->GetPtr() );
200 zstream.avail_in = reader->GetRemainingSize();
202 size_t total = 0l;
204 // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
205 #define MYBLOCK 1024
206 Bytef block[MYBLOCK];
207 int ret;
208 do {
209 zstream.avail_out = MYBLOCK;
210 zstream.next_out = block;
211 ret = inflate(&zstream, Z_NO_FLUSH);
213 if (ret != Z_STREAM_END && ret != Z_OK) {
214 ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
215 }
216 const size_t have = MYBLOCK - zstream.avail_out;
217 total += have;
218 dest = reinterpret_cast<Bytef*>( realloc(dest,total) );
219 memcpy(dest + total - have,block,have);
220 }
221 while (ret != Z_STREAM_END);
223 // terminate zlib
224 inflateEnd(&zstream);
226 // replace the input stream with a memory stream
227 stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(dest),total));
229 // .. and retry
230 stream->Read(magic,7,1);
231 if (strcmp(magic,"BLENDER")) {
232 ThrowException("Found no BLENDER magic word in decompressed GZIP file");
233 }
234 #endif
235 }
237 file.i64bit = (stream->Read(magic,1,1),magic[0]=='-');
238 file.little = (stream->Read(magic,1,1),magic[0]=='v');
240 stream->Read(magic,3,1);
241 magic[3] = '\0';
243 LogInfo((format(),"Blender version is ",magic[0],".",magic+1,
244 " (64bit: ",file.i64bit?"true":"false",
245 ", little endian: ",file.little?"true":"false",")"
246 ));
248 ParseBlendFile(file,stream);
250 Scene scene;
251 ExtractScene(scene,file);
253 ConvertBlendFile(pScene,scene,file);
254 }
256 // ------------------------------------------------------------------------------------------------
257 void BlenderImporter::ParseBlendFile(FileDatabase& out, boost::shared_ptr<IOStream> stream)
258 {
259 out.reader = boost::shared_ptr<StreamReaderAny>(new StreamReaderAny(stream,out.little));
261 DNAParser dna_reader(out);
262 const DNA* dna = NULL;
264 out.entries.reserve(128); { // even small BLEND files tend to consist of many file blocks
265 SectionParser parser(*out.reader.get(),out.i64bit);
267 // first parse the file in search for the DNA and insert all other sections into the database
268 while ((parser.Next(),1)) {
269 const FileBlockHead& head = parser.GetCurrent();
271 if (head.id == "ENDB") {
272 break; // only valid end of the file
273 }
274 else if (head.id == "DNA1") {
275 dna_reader.Parse();
276 dna = &dna_reader.GetDNA();
277 continue;
278 }
280 out.entries.push_back(head);
281 }
282 }
283 if (!dna) {
284 ThrowException("SDNA not found");
285 }
287 std::sort(out.entries.begin(),out.entries.end());
288 }
290 // ------------------------------------------------------------------------------------------------
291 void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
292 {
293 const FileBlockHead* block = NULL;
294 std::map<std::string,size_t>::const_iterator it = file.dna.indices.find("Scene");
295 if (it == file.dna.indices.end()) {
296 ThrowException("There is no `Scene` structure record");
297 }
299 const Structure& ss = file.dna.structures[(*it).second];
301 // we need a scene somewhere to start with.
302 for_each(const FileBlockHead& bl,file.entries) {
304 // Fix: using the DNA index is more reliable to locate scenes
305 //if (bl.id == "SC") {
307 if (bl.dna_index == (*it).second) {
308 block = &bl;
309 break;
310 }
311 }
313 if (!block) {
314 ThrowException("There is not a single `Scene` record to load");
315 }
317 file.reader->SetCurrentPos(block->start);
318 ss.Convert(out,file);
320 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
321 DefaultLogger::get()->info((format(),
322 "(Stats) Fields read: " ,file.stats().fields_read,
323 ", pointers resolved: " ,file.stats().pointers_resolved,
324 ", cache hits: " ,file.stats().cache_hits,
325 ", cached objects: " ,file.stats().cached_objects
326 ));
327 #endif
328 }
330 // ------------------------------------------------------------------------------------------------
331 void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file)
332 {
333 ConversionData conv(file);
335 // FIXME it must be possible to take the hierarchy directly from
336 // the file. This is terrible. Here, we're first looking for
337 // all objects which don't have parent objects at all -
338 std::deque<const Object*> no_parents;
339 for (boost::shared_ptr<Base> cur = boost::static_pointer_cast<Base> ( in.base.first ); cur; cur = cur->next) {
340 if (cur->object) {
341 if(!cur->object->parent) {
342 no_parents.push_back(cur->object.get());
343 }
344 else conv.objects.insert(cur->object.get());
345 }
346 }
347 for (boost::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
348 if (cur->object) {
349 if(cur->object->parent) {
350 conv.objects.insert(cur->object.get());
351 }
352 }
353 }
355 if (no_parents.empty()) {
356 ThrowException("Expected at least one object with no parent");
357 }
359 aiNode* root = out->mRootNode = new aiNode("<BlenderRoot>");
361 root->mNumChildren = static_cast<unsigned int>(no_parents.size());
362 root->mChildren = new aiNode*[root->mNumChildren]();
363 for (unsigned int i = 0; i < root->mNumChildren; ++i) {
364 root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4());
365 root->mChildren[i]->mParent = root;
366 }
368 BuildMaterials(conv);
370 if (conv.meshes->size()) {
371 out->mMeshes = new aiMesh*[out->mNumMeshes = static_cast<unsigned int>( conv.meshes->size() )];
372 std::copy(conv.meshes->begin(),conv.meshes->end(),out->mMeshes);
373 conv.meshes.dismiss();
374 }
376 if (conv.lights->size()) {
377 out->mLights = new aiLight*[out->mNumLights = static_cast<unsigned int>( conv.lights->size() )];
378 std::copy(conv.lights->begin(),conv.lights->end(),out->mLights);
379 conv.lights.dismiss();
380 }
382 if (conv.cameras->size()) {
383 out->mCameras = new aiCamera*[out->mNumCameras = static_cast<unsigned int>( conv.cameras->size() )];
384 std::copy(conv.cameras->begin(),conv.cameras->end(),out->mCameras);
385 conv.cameras.dismiss();
386 }
388 if (conv.materials->size()) {
389 out->mMaterials = new aiMaterial*[out->mNumMaterials = static_cast<unsigned int>( conv.materials->size() )];
390 std::copy(conv.materials->begin(),conv.materials->end(),out->mMaterials);
391 conv.materials.dismiss();
392 }
394 if (conv.textures->size()) {
395 out->mTextures = new aiTexture*[out->mNumTextures = static_cast<unsigned int>( conv.textures->size() )];
396 std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures);
397 conv.textures.dismiss();
398 }
400 // acknowledge that the scene might come out incomplete
401 // by Assimps definition of `complete`: blender scenes
402 // can consist of thousands of cameras or lights with
403 // not a single mesh between them.
404 if (!out->mNumMeshes) {
405 out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
406 }
407 }
409 // ------------------------------------------------------------------------------------------------
410 void BlenderImporter::ResolveImage(aiMaterial* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data)
411 {
412 (void)mat; (void)tex; (void)conv_data;
413 aiString name;
415 // check if the file contents are bundled with the BLEND file
416 if (img->packedfile) {
417 name.data[0] = '*';
418 name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size());
420 conv_data.textures->push_back(new aiTexture());
421 aiTexture* tex = conv_data.textures->back();
423 // usually 'img->name' will be the original file name of the embedded textures,
424 // so we can extract the file extension from it.
425 const size_t nlen = strlen( img->name );
426 const char* s = img->name+nlen, *e = s;
428 while (s >= img->name && *s != '.')--s;
430 tex->achFormatHint[0] = s+1>e ? '\0' : ::tolower( s[1] );
431 tex->achFormatHint[1] = s+2>e ? '\0' : ::tolower( s[2] );
432 tex->achFormatHint[2] = s+3>e ? '\0' : ::tolower( s[3] );
433 tex->achFormatHint[3] = '\0';
435 // tex->mHeight = 0;
436 tex->mWidth = img->packedfile->size;
437 uint8_t* ch = new uint8_t[tex->mWidth];
439 conv_data.db.reader->SetCurrentPos(static_cast<size_t>( img->packedfile->data->val));
440 conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
442 tex->pcData = reinterpret_cast<aiTexel*>(ch);
444 LogInfo("Reading embedded texture, original file was "+std::string(img->name));
445 }
446 else {
447 name = aiString( img->name );
448 }
449 out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
450 conv_data.next_texture[aiTextureType_DIFFUSE]++)
451 );
452 }
454 // ------------------------------------------------------------------------------------------------
455 void BlenderImporter::AddSentinelTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
456 {
457 (void)mat; (void)tex; (void)conv_data;
459 aiString name;
460 name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++,
461 GetTextureTypeDisplayString(tex->tex->type)
462 );
463 out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
464 conv_data.next_texture[aiTextureType_DIFFUSE]++)
465 );
466 }
468 // ------------------------------------------------------------------------------------------------
469 void BlenderImporter::ResolveTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
470 {
471 const Tex* rtex = tex->tex.get();
472 if(!rtex || !rtex->type) {
473 return;
474 }
476 // We can't support most of the texture types because they're mostly procedural.
477 // These are substituted by a dummy texture.
478 const char* dispnam = "";
479 switch( rtex->type )
480 {
481 // these are listed in blender's UI
482 case Tex::Type_CLOUDS :
483 case Tex::Type_WOOD :
484 case Tex::Type_MARBLE :
485 case Tex::Type_MAGIC :
486 case Tex::Type_BLEND :
487 case Tex::Type_STUCCI :
488 case Tex::Type_NOISE :
489 case Tex::Type_PLUGIN :
490 case Tex::Type_MUSGRAVE :
491 case Tex::Type_VORONOI :
492 case Tex::Type_DISTNOISE :
493 case Tex::Type_ENVMAP :
495 // these do no appear in the UI, why?
496 case Tex::Type_POINTDENSITY :
497 case Tex::Type_VOXELDATA :
499 LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam);
500 AddSentinelTexture(out, mat, tex, conv_data);
501 break;
503 case Tex::Type_IMAGE :
504 if (!rtex->ima) {
505 LogError("A texture claims to be an Image, but no image reference is given");
506 break;
507 }
508 ResolveImage(out, mat, tex, rtex->ima.get(),conv_data);
509 break;
511 default:
512 ai_assert(false);
513 };
514 }
516 // ------------------------------------------------------------------------------------------------
517 void BlenderImporter::BuildMaterials(ConversionData& conv_data)
518 {
519 conv_data.materials->reserve(conv_data.materials_raw.size());
521 // add a default material if necessary
522 unsigned int index = static_cast<unsigned int>( -1 );
523 for_each( aiMesh* mesh, conv_data.meshes.get() ) {
524 if (mesh->mMaterialIndex == static_cast<unsigned int>( -1 )) {
526 if (index == static_cast<unsigned int>( -1 )) {
528 // ok, we need to add a dedicated default material for some poor material-less meshes
529 boost::shared_ptr<Material> p(new Material());
530 strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME );
532 p->r = p->g = p->b = 0.6f;
533 p->specr = p->specg = p->specb = 0.6f;
534 p->ambr = p->ambg = p->ambb = 0.0f;
535 p->mirr = p->mirg = p->mirb = 0.0f;
536 p->emit = 0.f;
537 p->alpha = 0.f;
539 // XXX add more / or add default c'tor to Material
541 index = static_cast<unsigned int>( conv_data.materials_raw.size() );
542 conv_data.materials_raw.push_back(p);
544 LogInfo("Adding default material ...");
545 }
546 mesh->mMaterialIndex = index;
547 }
548 }
550 for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) {
552 // reset per material global counters
553 for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) {
554 conv_data.next_texture[i] = 0 ;
555 }
557 aiMaterial* mout = new aiMaterial();
558 conv_data.materials->push_back(mout);
560 // set material name
561 aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA'
562 mout->AddProperty(&name,AI_MATKEY_NAME);
565 // basic material colors
566 aiColor3D col(mat->r,mat->g,mat->b);
567 if (mat->r || mat->g || mat->b ) {
569 // Usually, zero diffuse color means no diffuse color at all in the equation.
570 // So we omit this member to express this intent.
571 mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE);
573 if (mat->emit) {
574 aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b) ;
575 mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE) ;
576 }
577 }
579 col = aiColor3D(mat->specr,mat->specg,mat->specb);
580 mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR);
582 // is hardness/shininess set?
583 if( mat->har ) {
584 const float har = mat->har;
585 mout->AddProperty(&har,1,AI_MATKEY_SHININESS);
586 }
588 col = aiColor3D(mat->ambr,mat->ambg,mat->ambb);
589 mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT);
591 col = aiColor3D(mat->mirr,mat->mirg,mat->mirb);
592 mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE);
594 for(size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
595 if (!mat->mtex[i]) {
596 continue;
597 }
599 ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data);
600 }
601 }
602 }
604 // ------------------------------------------------------------------------------------------------
605 void BlenderImporter::CheckActualType(const ElemBase* dt, const char* check)
606 {
607 ai_assert(dt);
608 if (strcmp(dt->dna_type,check)) {
609 ThrowException((format(),
610 "Expected object at ",std::hex,dt," to be of type `",check,
611 "`, but it claims to be a `",dt->dna_type,"`instead"
612 ));
613 }
614 }
616 // ------------------------------------------------------------------------------------------------
617 void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type)
618 {
619 LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" ));
620 }
622 // ------------------------------------------------------------------------------------------------
623 void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, const Mesh* mesh,
624 ConversionData& conv_data, TempArray<std::vector,aiMesh>& temp
625 )
626 {
627 typedef std::pair<const int,size_t> MyPair;
628 if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) {
629 return;
630 }
632 // some sanity checks
633 if (static_cast<size_t> ( mesh->totface ) > mesh->mface.size() ){
634 ThrowException("Number of faces is larger than the corresponding array");
635 }
637 if (static_cast<size_t> ( mesh->totvert ) > mesh->mvert.size()) {
638 ThrowException("Number of vertices is larger than the corresponding array");
639 }
641 if (static_cast<size_t> ( mesh->totloop ) > mesh->mloop.size()) {
642 ThrowException("Number of vertices is larger than the corresponding array");
643 }
645 // collect per-submesh numbers
646 std::map<int,size_t> per_mat;
647 std::map<int,size_t> per_mat_verts;
648 for (int i = 0; i < mesh->totface; ++i) {
650 const MFace& mf = mesh->mface[i];
651 per_mat[ mf.mat_nr ]++;
652 per_mat_verts[ mf.mat_nr ] += mf.v4?4:3;
653 }
655 for (int i = 0; i < mesh->totpoly; ++i) {
656 const MPoly& mp = mesh->mpoly[i];
657 per_mat[ mp.mat_nr ]++;
658 per_mat_verts[ mp.mat_nr ] += mp.totloop;
659 }
661 // ... and allocate the corresponding meshes
662 const size_t old = temp->size();
663 temp->reserve(temp->size() + per_mat.size());
665 std::map<size_t,size_t> mat_num_to_mesh_idx;
666 for_each(MyPair& it, per_mat) {
668 mat_num_to_mesh_idx[it.first] = temp->size();
669 temp->push_back(new aiMesh());
671 aiMesh* out = temp->back();
672 out->mVertices = new aiVector3D[per_mat_verts[it.first]];
673 out->mNormals = new aiVector3D[per_mat_verts[it.first]];
675 //out->mNumFaces = 0
676 //out->mNumVertices = 0
677 out->mFaces = new aiFace[it.second]();
679 // all submeshes created from this mesh are named equally. this allows
680 // curious users to recover the original adjacency.
681 out->mName = aiString(mesh->id.name+2);
682 // skip over the name prefix 'ME'
684 // resolve the material reference and add this material to the set of
685 // output materials. The (temporary) material index is the index
686 // of the material entry within the list of resolved materials.
687 if (mesh->mat) {
689 if (static_cast<size_t> ( it.first ) >= mesh->mat.size() ) {
690 ThrowException("Material index is out of range");
691 }
693 boost::shared_ptr<Material> mat = mesh->mat[it.first];
694 const std::deque< boost::shared_ptr<Material> >::iterator has = std::find(
695 conv_data.materials_raw.begin(),
696 conv_data.materials_raw.end(),mat
697 );
699 if (has != conv_data.materials_raw.end()) {
700 out->mMaterialIndex = static_cast<unsigned int>( std::distance(conv_data.materials_raw.begin(),has));
701 }
702 else {
703 out->mMaterialIndex = static_cast<unsigned int>( conv_data.materials_raw.size() );
704 conv_data.materials_raw.push_back(mat);
705 }
706 }
707 else out->mMaterialIndex = static_cast<unsigned int>( -1 );
708 }
710 for (int i = 0; i < mesh->totface; ++i) {
712 const MFace& mf = mesh->mface[i];
714 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
715 aiFace& f = out->mFaces[out->mNumFaces++];
717 f.mIndices = new unsigned int[ f.mNumIndices = mf.v4?4:3 ];
718 aiVector3D* vo = out->mVertices + out->mNumVertices;
719 aiVector3D* vn = out->mNormals + out->mNumVertices;
721 // XXX we can't fold this easily, because we are restricted
722 // to the member names from the BLEND file (v1,v2,v3,v4)
723 // which are assigned by the genblenddna.py script and
724 // cannot be changed without breaking the entire
725 // import process.
727 if (mf.v1 >= mesh->totvert) {
728 ThrowException("Vertex index v1 out of range");
729 }
730 const MVert* v = &mesh->mvert[mf.v1];
731 vo->x = v->co[0];
732 vo->y = v->co[1];
733 vo->z = v->co[2];
734 vn->x = v->no[0];
735 vn->y = v->no[1];
736 vn->z = v->no[2];
737 f.mIndices[0] = out->mNumVertices++;
738 ++vo;
739 ++vn;
741 // if (f.mNumIndices >= 2) {
742 if (mf.v2 >= mesh->totvert) {
743 ThrowException("Vertex index v2 out of range");
744 }
745 v = &mesh->mvert[mf.v2];
746 vo->x = v->co[0];
747 vo->y = v->co[1];
748 vo->z = v->co[2];
749 vn->x = v->no[0];
750 vn->y = v->no[1];
751 vn->z = v->no[2];
752 f.mIndices[1] = out->mNumVertices++;
753 ++vo;
754 ++vn;
756 if (mf.v3 >= mesh->totvert) {
757 ThrowException("Vertex index v3 out of range");
758 }
759 // if (f.mNumIndices >= 3) {
760 v = &mesh->mvert[mf.v3];
761 vo->x = v->co[0];
762 vo->y = v->co[1];
763 vo->z = v->co[2];
764 vn->x = v->no[0];
765 vn->y = v->no[1];
766 vn->z = v->no[2];
767 f.mIndices[2] = out->mNumVertices++;
768 ++vo;
769 ++vn;
771 if (mf.v4 >= mesh->totvert) {
772 ThrowException("Vertex index v4 out of range");
773 }
774 // if (f.mNumIndices >= 4) {
775 if (mf.v4) {
776 v = &mesh->mvert[mf.v4];
777 vo->x = v->co[0];
778 vo->y = v->co[1];
779 vo->z = v->co[2];
780 vn->x = v->no[0];
781 vn->y = v->no[1];
782 vn->z = v->no[2];
783 f.mIndices[3] = out->mNumVertices++;
784 ++vo;
785 ++vn;
787 out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
788 }
789 else out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
791 // }
792 // }
793 // }
794 }
796 for (int i = 0; i < mesh->totpoly; ++i) {
798 const MPoly& mf = mesh->mpoly[i];
800 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
801 aiFace& f = out->mFaces[out->mNumFaces++];
803 f.mIndices = new unsigned int[ f.mNumIndices = mf.totloop ];
804 aiVector3D* vo = out->mVertices + out->mNumVertices;
805 aiVector3D* vn = out->mNormals + out->mNumVertices;
807 // XXX we can't fold this easily, because we are restricted
808 // to the member names from the BLEND file (v1,v2,v3,v4)
809 // which are assigned by the genblenddna.py script and
810 // cannot be changed without breaking the entire
811 // import process.
812 for (int j = 0;j < mf.totloop; ++j)
813 {
814 const MLoop& loop = mesh->mloop[mf.loopstart + j];
816 if (loop.v >= mesh->totvert) {
817 ThrowException("Vertex index out of range");
818 }
820 const MVert& v = mesh->mvert[loop.v];
822 vo->x = v.co[0];
823 vo->y = v.co[1];
824 vo->z = v.co[2];
825 vn->x = v.no[0];
826 vn->y = v.no[1];
827 vn->z = v.no[2];
828 f.mIndices[j] = out->mNumVertices++;
830 ++vo;
831 ++vn;
833 }
834 if (mf.totloop == 3)
835 {
836 out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
837 }
838 else
839 {
840 out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
841 }
842 }
844 // collect texture coordinates, they're stored in a separate per-face buffer
845 if (mesh->mtface || mesh->mloopuv) {
846 if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
847 ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)");
848 }
849 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
850 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
852 (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
853 (*it)->mNumFaces = (*it)->mNumVertices = 0;
854 }
856 for (int i = 0; i < mesh->totface; ++i) {
857 const MTFace* v = &mesh->mtface[i];
859 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
860 const aiFace& f = out->mFaces[out->mNumFaces++];
862 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
863 for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
864 vo->x = v->uv[i][0];
865 vo->y = v->uv[i][1];
866 }
867 }
869 for (int i = 0; i < mesh->totpoly; ++i) {
870 const MPoly& v = mesh->mpoly[i];
871 aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
872 const aiFace& f = out->mFaces[out->mNumFaces++];
874 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
875 for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
876 const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
877 vo->x = uv.uv[0];
878 vo->y = uv.uv[1];
879 }
881 }
882 }
884 // collect texture coordinates, old-style (marked as deprecated in current blender sources)
885 if (mesh->tface) {
886 if (mesh->totface > static_cast<int> ( mesh->tface.size())) {
887 ThrowException("Number of faces is larger than the corresponding UV face array (#2)");
888 }
889 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
890 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
892 (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
893 (*it)->mNumFaces = (*it)->mNumVertices = 0;
894 }
896 for (int i = 0; i < mesh->totface; ++i) {
897 const TFace* v = &mesh->tface[i];
899 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
900 const aiFace& f = out->mFaces[out->mNumFaces++];
902 aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
903 for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
904 vo->x = v->uv[i][0];
905 vo->y = v->uv[i][1];
906 }
907 }
908 }
910 // collect vertex colors, stored separately as well
911 if (mesh->mcol || mesh->mloopcol) {
912 if (mesh->totface > static_cast<int> ( (mesh->mcol.size()/4)) ) {
913 ThrowException("Number of faces is larger than the corresponding color face array");
914 }
915 for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
916 ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
918 (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices];
919 (*it)->mNumFaces = (*it)->mNumVertices = 0;
920 }
922 for (int i = 0; i < mesh->totface; ++i) {
924 aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
925 const aiFace& f = out->mFaces[out->mNumFaces++];
927 aiColor4D* vo = &out->mColors[0][out->mNumVertices];
928 for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo,++out->mNumVertices) {
929 const MCol* col = &mesh->mcol[(i<<2)+n];
931 vo->r = col->r;
932 vo->g = col->g;
933 vo->b = col->b;
934 vo->a = col->a;
935 }
936 for (unsigned int n = f.mNumIndices; n < 4; ++n);
937 }
939 for (int i = 0; i < mesh->totpoly; ++i) {
940 const MPoly& v = mesh->mpoly[i];
941 aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
942 const aiFace& f = out->mFaces[out->mNumFaces++];
944 aiColor4D* vo = &out->mColors[0][out->mNumVertices];
945 for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
946 const MLoopCol& col = mesh->mloopcol[v.loopstart + j];
947 vo->r = col.r;
948 vo->g = col.g;
949 vo->b = col.b;
950 vo->a = col.a;
951 }
953 }
955 }
957 return;
958 }
960 // ------------------------------------------------------------------------------------------------
961 aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* /*obj*/, const Camera* /*mesh*/, ConversionData& /*conv_data*/)
962 {
963 ScopeGuard<aiCamera> out(new aiCamera());
965 return NULL ; //out.dismiss();
966 }
968 // ------------------------------------------------------------------------------------------------
969 aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* /*obj*/, const Lamp* /*mesh*/, ConversionData& /*conv_data*/)
970 {
971 ScopeGuard<aiLight> out(new aiLight());
973 return NULL ; //out.dismiss();
974 }
976 // ------------------------------------------------------------------------------------------------
977 aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform)
978 {
979 std::deque<const Object*> children;
980 for(std::set<const Object*>::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
981 const Object* object = *it;
982 if (object->parent == obj) {
983 children.push_back(object);
985 conv_data.objects.erase(it++);
986 continue;
987 }
988 ++it;
989 }
991 ScopeGuard<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
992 if (obj->data) {
993 switch (obj->type)
994 {
995 case Object :: Type_EMPTY:
996 break; // do nothing
999 // supported object types
1000 case Object :: Type_MESH: {
1001 const size_t old = conv_data.meshes->size();
1003 CheckActualType(obj->data.get(),"Mesh");
1004 ConvertMesh(in,obj,static_cast<const Mesh*>(obj->data.get()),conv_data,conv_data.meshes);
1006 if (conv_data.meshes->size() > old) {
1007 node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size()-old)];
1008 for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
1009 node->mMeshes[i] = i + old;
1010 }
1011 }}
1012 break;
1013 case Object :: Type_LAMP: {
1014 CheckActualType(obj->data.get(),"Lamp");
1015 aiLight* mesh = ConvertLight(in,obj,static_cast<const Lamp*>(
1016 obj->data.get()),conv_data);
1018 if (mesh) {
1019 conv_data.lights->push_back(mesh);
1020 }}
1021 break;
1022 case Object :: Type_CAMERA: {
1023 CheckActualType(obj->data.get(),"Camera");
1024 aiCamera* mesh = ConvertCamera(in,obj,static_cast<const Camera*>(
1025 obj->data.get()),conv_data);
1027 if (mesh) {
1028 conv_data.cameras->push_back(mesh);
1029 }}
1030 break;
1033 // unsupported object types / log, but do not break
1034 case Object :: Type_CURVE:
1035 NotSupportedObjectType(obj,"Curve");
1036 break;
1037 case Object :: Type_SURF:
1038 NotSupportedObjectType(obj,"Surface");
1039 break;
1040 case Object :: Type_FONT:
1041 NotSupportedObjectType(obj,"Font");
1042 break;
1043 case Object :: Type_MBALL:
1044 NotSupportedObjectType(obj,"MetaBall");
1045 break;
1046 case Object :: Type_WAVE:
1047 NotSupportedObjectType(obj,"Wave");
1048 break;
1049 case Object :: Type_LATTICE:
1050 NotSupportedObjectType(obj,"Lattice");
1051 break;
1053 // invalid or unknown type
1054 default:
1055 break;
1056 }
1057 }
1059 for(unsigned int x = 0; x < 4; ++x) {
1060 for(unsigned int y = 0; y < 4; ++y) {
1061 node->mTransformation[y][x] = obj->obmat[x][y];
1062 }
1063 }
1065 aiMatrix4x4 m = parentTransform;
1066 m = m.Inverse();
1068 node->mTransformation = m*node->mTransformation;
1070 if (children.size()) {
1071 node->mNumChildren = static_cast<unsigned int>(children.size());
1072 aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren]();
1073 for_each (const Object* nobj,children) {
1074 *nd = ConvertNode(in,nobj,conv_data,node->mTransformation * parentTransform);
1075 (*nd++)->mParent = node;
1076 }
1077 }
1079 // apply modifiers
1080 modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
1082 return node.dismiss();
1083 }
1086 #endif