nuclear@0: nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file BlenderLoader.cpp nuclear@0: * @brief Implementation of the Blender3D importer class. nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: //#define ASSIMP_BUILD_NO_COMPRESSED_BLEND nuclear@0: // Uncomment this to disable support for (gzip)compressed .BLEND files nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER nuclear@0: nuclear@0: #include "BlenderIntermediate.h" nuclear@0: #include "BlenderModifier.h" nuclear@0: nuclear@0: #include "StreamReader.h" nuclear@0: #include "MemoryIOWrapper.h" nuclear@0: nuclear@0: // zlib is needed for compressed blend files nuclear@0: #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND nuclear@0: # ifdef ASSIMP_BUILD_NO_OWN_ZLIB nuclear@0: # include nuclear@0: # else nuclear@0: # include "../contrib/zlib/zlib.h" nuclear@0: # endif nuclear@0: #endif nuclear@0: nuclear@0: namespace Assimp { nuclear@0: template<> const std::string LogFunctions::log_prefix = "BLEND: "; nuclear@0: } nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::Blender; nuclear@0: using namespace Assimp::Formatter; nuclear@0: nuclear@0: static const aiImporterDesc blenderDesc = { nuclear@0: "Blender 3D Importer \nhttp://www.blender3d.org", nuclear@0: "", nuclear@0: "", nuclear@0: "No animation support yet", nuclear@0: aiImporterFlags_SupportBinaryFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 2, nuclear@0: 50, nuclear@0: "blend" nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: BlenderImporter::BlenderImporter() nuclear@0: : modifier_cache(new BlenderModifierShowcase()) nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: BlenderImporter::~BlenderImporter() nuclear@0: { nuclear@0: delete modifier_cache; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: const std::string& extension = GetExtension(pFile); nuclear@0: if (extension == "blend") { nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: else if ((!extension.length() || checkSig) && pIOHandler) { nuclear@0: // note: this won't catch compressed files nuclear@0: const char* tokens[] = {"BLENDER"}; nuclear@0: return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // List all extensions handled by this loader nuclear@0: void BlenderImporter::GetExtensionList(std::set& app) nuclear@0: { nuclear@0: app.insert("blend"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Loader registry entry nuclear@0: const aiImporterDesc* BlenderImporter::GetInfo () const nuclear@0: { nuclear@0: return &blenderDesc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup configuration properties for the loader nuclear@0: void BlenderImporter::SetupProperties(const Importer* /*pImp*/) nuclear@0: { nuclear@0: // nothing to be done for the moment nuclear@0: } nuclear@0: nuclear@0: struct free_it nuclear@0: { nuclear@0: free_it(void* free) : free(free) {} nuclear@0: ~free_it() { nuclear@0: ::free(this->free); nuclear@0: } nuclear@0: nuclear@0: void* free; nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void BlenderImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND nuclear@0: Bytef* dest = NULL; nuclear@0: free_it free_it_really(dest); nuclear@0: #endif nuclear@0: nuclear@0: FileDatabase file; nuclear@0: boost::shared_ptr stream(pIOHandler->Open(pFile,"rb")); nuclear@0: if (!stream) { nuclear@0: ThrowException("Could not open file for reading"); nuclear@0: } nuclear@0: nuclear@0: char magic[8] = {0}; nuclear@0: stream->Read(magic,7,1); nuclear@0: if (strcmp(magic,"BLENDER")) { nuclear@0: // Check for presence of the gzip header. If yes, assume it is a nuclear@0: // compressed blend file and try uncompressing it, else fail. This is to nuclear@0: // avoid uncompressing random files which our loader might end up with. nuclear@0: #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND nuclear@0: ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"); nuclear@0: #else nuclear@0: nuclear@0: if (magic[0] != 0x1f || static_cast(magic[1]) != 0x8b) { nuclear@0: ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either"); nuclear@0: } nuclear@0: nuclear@0: LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file"); nuclear@0: if (magic[2] != 8) { nuclear@0: ThrowException("Unsupported GZIP compression method"); nuclear@0: } nuclear@0: nuclear@0: // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer nuclear@0: stream->Seek(0L,aiOrigin_SET); nuclear@0: boost::shared_ptr reader = boost::shared_ptr(new StreamReaderLE(stream)); nuclear@0: nuclear@0: // build a zlib stream nuclear@0: z_stream zstream; nuclear@0: zstream.opaque = Z_NULL; nuclear@0: zstream.zalloc = Z_NULL; nuclear@0: zstream.zfree = Z_NULL; nuclear@0: zstream.data_type = Z_BINARY; nuclear@0: nuclear@0: // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib nuclear@0: inflateInit2(&zstream, 16+MAX_WBITS); nuclear@0: nuclear@0: zstream.next_in = reinterpret_cast( reader->GetPtr() ); nuclear@0: zstream.avail_in = reader->GetRemainingSize(); nuclear@0: nuclear@0: size_t total = 0l; nuclear@0: nuclear@0: // and decompress the data .... do 1k chunks in the hope that we won't kill the stack nuclear@0: #define MYBLOCK 1024 nuclear@0: Bytef block[MYBLOCK]; nuclear@0: int ret; nuclear@0: do { nuclear@0: zstream.avail_out = MYBLOCK; nuclear@0: zstream.next_out = block; nuclear@0: ret = inflate(&zstream, Z_NO_FLUSH); nuclear@0: nuclear@0: if (ret != Z_STREAM_END && ret != Z_OK) { nuclear@0: ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file"); nuclear@0: } nuclear@0: const size_t have = MYBLOCK - zstream.avail_out; nuclear@0: total += have; nuclear@0: dest = reinterpret_cast( realloc(dest,total) ); nuclear@0: memcpy(dest + total - have,block,have); nuclear@0: } nuclear@0: while (ret != Z_STREAM_END); nuclear@0: nuclear@0: // terminate zlib nuclear@0: inflateEnd(&zstream); nuclear@0: nuclear@0: // replace the input stream with a memory stream nuclear@0: stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); nuclear@0: nuclear@0: // .. and retry nuclear@0: stream->Read(magic,7,1); nuclear@0: if (strcmp(magic,"BLENDER")) { nuclear@0: ThrowException("Found no BLENDER magic word in decompressed GZIP file"); nuclear@0: } nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: file.i64bit = (stream->Read(magic,1,1),magic[0]=='-'); nuclear@0: file.little = (stream->Read(magic,1,1),magic[0]=='v'); nuclear@0: nuclear@0: stream->Read(magic,3,1); nuclear@0: magic[3] = '\0'; nuclear@0: nuclear@0: LogInfo((format(),"Blender version is ",magic[0],".",magic+1, nuclear@0: " (64bit: ",file.i64bit?"true":"false", nuclear@0: ", little endian: ",file.little?"true":"false",")" nuclear@0: )); nuclear@0: nuclear@0: ParseBlendFile(file,stream); nuclear@0: nuclear@0: Scene scene; nuclear@0: ExtractScene(scene,file); nuclear@0: nuclear@0: ConvertBlendFile(pScene,scene,file); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ParseBlendFile(FileDatabase& out, boost::shared_ptr stream) nuclear@0: { nuclear@0: out.reader = boost::shared_ptr(new StreamReaderAny(stream,out.little)); nuclear@0: nuclear@0: DNAParser dna_reader(out); nuclear@0: const DNA* dna = NULL; nuclear@0: nuclear@0: out.entries.reserve(128); { // even small BLEND files tend to consist of many file blocks nuclear@0: SectionParser parser(*out.reader.get(),out.i64bit); nuclear@0: nuclear@0: // first parse the file in search for the DNA and insert all other sections into the database nuclear@0: while ((parser.Next(),1)) { nuclear@0: const FileBlockHead& head = parser.GetCurrent(); nuclear@0: nuclear@0: if (head.id == "ENDB") { nuclear@0: break; // only valid end of the file nuclear@0: } nuclear@0: else if (head.id == "DNA1") { nuclear@0: dna_reader.Parse(); nuclear@0: dna = &dna_reader.GetDNA(); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: out.entries.push_back(head); nuclear@0: } nuclear@0: } nuclear@0: if (!dna) { nuclear@0: ThrowException("SDNA not found"); nuclear@0: } nuclear@0: nuclear@0: std::sort(out.entries.begin(),out.entries.end()); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file) nuclear@0: { nuclear@0: const FileBlockHead* block = NULL; nuclear@0: std::map::const_iterator it = file.dna.indices.find("Scene"); nuclear@0: if (it == file.dna.indices.end()) { nuclear@0: ThrowException("There is no `Scene` structure record"); nuclear@0: } nuclear@0: nuclear@0: const Structure& ss = file.dna.structures[(*it).second]; nuclear@0: nuclear@0: // we need a scene somewhere to start with. nuclear@0: for_each(const FileBlockHead& bl,file.entries) { nuclear@0: nuclear@0: // Fix: using the DNA index is more reliable to locate scenes nuclear@0: //if (bl.id == "SC") { nuclear@0: nuclear@0: if (bl.dna_index == (*it).second) { nuclear@0: block = &bl; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!block) { nuclear@0: ThrowException("There is not a single `Scene` record to load"); nuclear@0: } nuclear@0: nuclear@0: file.reader->SetCurrentPos(block->start); nuclear@0: ss.Convert(out,file); nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_BLENDER_NO_STATS nuclear@0: DefaultLogger::get()->info((format(), nuclear@0: "(Stats) Fields read: " ,file.stats().fields_read, nuclear@0: ", pointers resolved: " ,file.stats().pointers_resolved, nuclear@0: ", cache hits: " ,file.stats().cache_hits, nuclear@0: ", cached objects: " ,file.stats().cached_objects nuclear@0: )); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file) nuclear@0: { nuclear@0: ConversionData conv(file); nuclear@0: nuclear@0: // FIXME it must be possible to take the hierarchy directly from nuclear@0: // the file. This is terrible. Here, we're first looking for nuclear@0: // all objects which don't have parent objects at all - nuclear@0: std::deque no_parents; nuclear@0: for (boost::shared_ptr cur = boost::static_pointer_cast ( in.base.first ); cur; cur = cur->next) { nuclear@0: if (cur->object) { nuclear@0: if(!cur->object->parent) { nuclear@0: no_parents.push_back(cur->object.get()); nuclear@0: } nuclear@0: else conv.objects.insert(cur->object.get()); nuclear@0: } nuclear@0: } nuclear@0: for (boost::shared_ptr cur = in.basact; cur; cur = cur->next) { nuclear@0: if (cur->object) { nuclear@0: if(cur->object->parent) { nuclear@0: conv.objects.insert(cur->object.get()); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (no_parents.empty()) { nuclear@0: ThrowException("Expected at least one object with no parent"); nuclear@0: } nuclear@0: nuclear@0: aiNode* root = out->mRootNode = new aiNode(""); nuclear@0: nuclear@0: root->mNumChildren = static_cast(no_parents.size()); nuclear@0: root->mChildren = new aiNode*[root->mNumChildren](); nuclear@0: for (unsigned int i = 0; i < root->mNumChildren; ++i) { nuclear@0: root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4()); nuclear@0: root->mChildren[i]->mParent = root; nuclear@0: } nuclear@0: nuclear@0: BuildMaterials(conv); nuclear@0: nuclear@0: if (conv.meshes->size()) { nuclear@0: out->mMeshes = new aiMesh*[out->mNumMeshes = static_cast( conv.meshes->size() )]; nuclear@0: std::copy(conv.meshes->begin(),conv.meshes->end(),out->mMeshes); nuclear@0: conv.meshes.dismiss(); nuclear@0: } nuclear@0: nuclear@0: if (conv.lights->size()) { nuclear@0: out->mLights = new aiLight*[out->mNumLights = static_cast( conv.lights->size() )]; nuclear@0: std::copy(conv.lights->begin(),conv.lights->end(),out->mLights); nuclear@0: conv.lights.dismiss(); nuclear@0: } nuclear@0: nuclear@0: if (conv.cameras->size()) { nuclear@0: out->mCameras = new aiCamera*[out->mNumCameras = static_cast( conv.cameras->size() )]; nuclear@0: std::copy(conv.cameras->begin(),conv.cameras->end(),out->mCameras); nuclear@0: conv.cameras.dismiss(); nuclear@0: } nuclear@0: nuclear@0: if (conv.materials->size()) { nuclear@0: out->mMaterials = new aiMaterial*[out->mNumMaterials = static_cast( conv.materials->size() )]; nuclear@0: std::copy(conv.materials->begin(),conv.materials->end(),out->mMaterials); nuclear@0: conv.materials.dismiss(); nuclear@0: } nuclear@0: nuclear@0: if (conv.textures->size()) { nuclear@0: out->mTextures = new aiTexture*[out->mNumTextures = static_cast( conv.textures->size() )]; nuclear@0: std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures); nuclear@0: conv.textures.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // acknowledge that the scene might come out incomplete nuclear@0: // by Assimps definition of `complete`: blender scenes nuclear@0: // can consist of thousands of cameras or lights with nuclear@0: // not a single mesh between them. nuclear@0: if (!out->mNumMeshes) { nuclear@0: out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ResolveImage(aiMaterial* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data) nuclear@0: { nuclear@0: (void)mat; (void)tex; (void)conv_data; nuclear@0: aiString name; nuclear@0: nuclear@0: // check if the file contents are bundled with the BLEND file nuclear@0: if (img->packedfile) { nuclear@0: name.data[0] = '*'; nuclear@0: name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size()); nuclear@0: nuclear@0: conv_data.textures->push_back(new aiTexture()); nuclear@0: aiTexture* tex = conv_data.textures->back(); nuclear@0: nuclear@0: // usually 'img->name' will be the original file name of the embedded textures, nuclear@0: // so we can extract the file extension from it. nuclear@0: const size_t nlen = strlen( img->name ); nuclear@0: const char* s = img->name+nlen, *e = s; nuclear@0: nuclear@0: while (s >= img->name && *s != '.')--s; nuclear@0: nuclear@0: tex->achFormatHint[0] = s+1>e ? '\0' : ::tolower( s[1] ); nuclear@0: tex->achFormatHint[1] = s+2>e ? '\0' : ::tolower( s[2] ); nuclear@0: tex->achFormatHint[2] = s+3>e ? '\0' : ::tolower( s[3] ); nuclear@0: tex->achFormatHint[3] = '\0'; nuclear@0: nuclear@0: // tex->mHeight = 0; nuclear@0: tex->mWidth = img->packedfile->size; nuclear@0: uint8_t* ch = new uint8_t[tex->mWidth]; nuclear@0: nuclear@0: conv_data.db.reader->SetCurrentPos(static_cast( img->packedfile->data->val)); nuclear@0: conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth); nuclear@0: nuclear@0: tex->pcData = reinterpret_cast(ch); nuclear@0: nuclear@0: LogInfo("Reading embedded texture, original file was "+std::string(img->name)); nuclear@0: } nuclear@0: else { nuclear@0: name = aiString( img->name ); nuclear@0: } nuclear@0: out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE( nuclear@0: conv_data.next_texture[aiTextureType_DIFFUSE]++) nuclear@0: ); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::AddSentinelTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data) nuclear@0: { nuclear@0: (void)mat; (void)tex; (void)conv_data; nuclear@0: nuclear@0: aiString name; nuclear@0: name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++, nuclear@0: GetTextureTypeDisplayString(tex->tex->type) nuclear@0: ); nuclear@0: out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE( nuclear@0: conv_data.next_texture[aiTextureType_DIFFUSE]++) nuclear@0: ); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ResolveTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data) nuclear@0: { nuclear@0: const Tex* rtex = tex->tex.get(); nuclear@0: if(!rtex || !rtex->type) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // We can't support most of the texture types because they're mostly procedural. nuclear@0: // These are substituted by a dummy texture. nuclear@0: const char* dispnam = ""; nuclear@0: switch( rtex->type ) nuclear@0: { nuclear@0: // these are listed in blender's UI nuclear@0: case Tex::Type_CLOUDS : nuclear@0: case Tex::Type_WOOD : nuclear@0: case Tex::Type_MARBLE : nuclear@0: case Tex::Type_MAGIC : nuclear@0: case Tex::Type_BLEND : nuclear@0: case Tex::Type_STUCCI : nuclear@0: case Tex::Type_NOISE : nuclear@0: case Tex::Type_PLUGIN : nuclear@0: case Tex::Type_MUSGRAVE : nuclear@0: case Tex::Type_VORONOI : nuclear@0: case Tex::Type_DISTNOISE : nuclear@0: case Tex::Type_ENVMAP : nuclear@0: nuclear@0: // these do no appear in the UI, why? nuclear@0: case Tex::Type_POINTDENSITY : nuclear@0: case Tex::Type_VOXELDATA : nuclear@0: nuclear@0: LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam); nuclear@0: AddSentinelTexture(out, mat, tex, conv_data); nuclear@0: break; nuclear@0: nuclear@0: case Tex::Type_IMAGE : nuclear@0: if (!rtex->ima) { nuclear@0: LogError("A texture claims to be an Image, but no image reference is given"); nuclear@0: break; nuclear@0: } nuclear@0: ResolveImage(out, mat, tex, rtex->ima.get(),conv_data); nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: ai_assert(false); nuclear@0: }; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::BuildMaterials(ConversionData& conv_data) nuclear@0: { nuclear@0: conv_data.materials->reserve(conv_data.materials_raw.size()); nuclear@0: nuclear@0: // add a default material if necessary nuclear@0: unsigned int index = static_cast( -1 ); nuclear@0: for_each( aiMesh* mesh, conv_data.meshes.get() ) { nuclear@0: if (mesh->mMaterialIndex == static_cast( -1 )) { nuclear@0: nuclear@0: if (index == static_cast( -1 )) { nuclear@0: nuclear@0: // ok, we need to add a dedicated default material for some poor material-less meshes nuclear@0: boost::shared_ptr p(new Material()); nuclear@0: strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME ); nuclear@0: nuclear@0: p->r = p->g = p->b = 0.6f; nuclear@0: p->specr = p->specg = p->specb = 0.6f; nuclear@0: p->ambr = p->ambg = p->ambb = 0.0f; nuclear@0: p->mirr = p->mirg = p->mirb = 0.0f; nuclear@0: p->emit = 0.f; nuclear@0: p->alpha = 0.f; nuclear@0: nuclear@0: // XXX add more / or add default c'tor to Material nuclear@0: nuclear@0: index = static_cast( conv_data.materials_raw.size() ); nuclear@0: conv_data.materials_raw.push_back(p); nuclear@0: nuclear@0: LogInfo("Adding default material ..."); nuclear@0: } nuclear@0: mesh->mMaterialIndex = index; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: for_each(boost::shared_ptr mat, conv_data.materials_raw) { nuclear@0: nuclear@0: // reset per material global counters nuclear@0: for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) { nuclear@0: conv_data.next_texture[i] = 0 ; nuclear@0: } nuclear@0: nuclear@0: aiMaterial* mout = new aiMaterial(); nuclear@0: conv_data.materials->push_back(mout); nuclear@0: nuclear@0: // set material name nuclear@0: aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA' nuclear@0: mout->AddProperty(&name,AI_MATKEY_NAME); nuclear@0: nuclear@0: nuclear@0: // basic material colors nuclear@0: aiColor3D col(mat->r,mat->g,mat->b); nuclear@0: if (mat->r || mat->g || mat->b ) { nuclear@0: nuclear@0: // Usually, zero diffuse color means no diffuse color at all in the equation. nuclear@0: // So we omit this member to express this intent. nuclear@0: mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE); nuclear@0: nuclear@0: if (mat->emit) { nuclear@0: aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b) ; nuclear@0: mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE) ; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: col = aiColor3D(mat->specr,mat->specg,mat->specb); nuclear@0: mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR); nuclear@0: nuclear@0: // is hardness/shininess set? nuclear@0: if( mat->har ) { nuclear@0: const float har = mat->har; nuclear@0: mout->AddProperty(&har,1,AI_MATKEY_SHININESS); nuclear@0: } nuclear@0: nuclear@0: col = aiColor3D(mat->ambr,mat->ambg,mat->ambb); nuclear@0: mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT); nuclear@0: nuclear@0: col = aiColor3D(mat->mirr,mat->mirg,mat->mirb); nuclear@0: mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE); nuclear@0: nuclear@0: for(size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) { nuclear@0: if (!mat->mtex[i]) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::CheckActualType(const ElemBase* dt, const char* check) nuclear@0: { nuclear@0: ai_assert(dt); nuclear@0: if (strcmp(dt->dna_type,check)) { nuclear@0: ThrowException((format(), nuclear@0: "Expected object at ",std::hex,dt," to be of type `",check, nuclear@0: "`, but it claims to be a `",dt->dna_type,"`instead" nuclear@0: )); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type) nuclear@0: { nuclear@0: LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" )); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, const Mesh* mesh, nuclear@0: ConversionData& conv_data, TempArray& temp nuclear@0: ) nuclear@0: { nuclear@0: typedef std::pair MyPair; nuclear@0: if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // some sanity checks nuclear@0: if (static_cast ( mesh->totface ) > mesh->mface.size() ){ nuclear@0: ThrowException("Number of faces is larger than the corresponding array"); nuclear@0: } nuclear@0: nuclear@0: if (static_cast ( mesh->totvert ) > mesh->mvert.size()) { nuclear@0: ThrowException("Number of vertices is larger than the corresponding array"); nuclear@0: } nuclear@0: nuclear@0: if (static_cast ( mesh->totloop ) > mesh->mloop.size()) { nuclear@0: ThrowException("Number of vertices is larger than the corresponding array"); nuclear@0: } nuclear@0: nuclear@0: // collect per-submesh numbers nuclear@0: std::map per_mat; nuclear@0: std::map per_mat_verts; nuclear@0: for (int i = 0; i < mesh->totface; ++i) { nuclear@0: nuclear@0: const MFace& mf = mesh->mface[i]; nuclear@0: per_mat[ mf.mat_nr ]++; nuclear@0: per_mat_verts[ mf.mat_nr ] += mf.v4?4:3; nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totpoly; ++i) { nuclear@0: const MPoly& mp = mesh->mpoly[i]; nuclear@0: per_mat[ mp.mat_nr ]++; nuclear@0: per_mat_verts[ mp.mat_nr ] += mp.totloop; nuclear@0: } nuclear@0: nuclear@0: // ... and allocate the corresponding meshes nuclear@0: const size_t old = temp->size(); nuclear@0: temp->reserve(temp->size() + per_mat.size()); nuclear@0: nuclear@0: std::map mat_num_to_mesh_idx; nuclear@0: for_each(MyPair& it, per_mat) { nuclear@0: nuclear@0: mat_num_to_mesh_idx[it.first] = temp->size(); nuclear@0: temp->push_back(new aiMesh()); nuclear@0: nuclear@0: aiMesh* out = temp->back(); nuclear@0: out->mVertices = new aiVector3D[per_mat_verts[it.first]]; nuclear@0: out->mNormals = new aiVector3D[per_mat_verts[it.first]]; nuclear@0: nuclear@0: //out->mNumFaces = 0 nuclear@0: //out->mNumVertices = 0 nuclear@0: out->mFaces = new aiFace[it.second](); nuclear@0: nuclear@0: // all submeshes created from this mesh are named equally. this allows nuclear@0: // curious users to recover the original adjacency. nuclear@0: out->mName = aiString(mesh->id.name+2); nuclear@0: // skip over the name prefix 'ME' nuclear@0: nuclear@0: // resolve the material reference and add this material to the set of nuclear@0: // output materials. The (temporary) material index is the index nuclear@0: // of the material entry within the list of resolved materials. nuclear@0: if (mesh->mat) { nuclear@0: nuclear@0: if (static_cast ( it.first ) >= mesh->mat.size() ) { nuclear@0: ThrowException("Material index is out of range"); nuclear@0: } nuclear@0: nuclear@0: boost::shared_ptr mat = mesh->mat[it.first]; nuclear@0: const std::deque< boost::shared_ptr >::iterator has = std::find( nuclear@0: conv_data.materials_raw.begin(), nuclear@0: conv_data.materials_raw.end(),mat nuclear@0: ); nuclear@0: nuclear@0: if (has != conv_data.materials_raw.end()) { nuclear@0: out->mMaterialIndex = static_cast( std::distance(conv_data.materials_raw.begin(),has)); nuclear@0: } nuclear@0: else { nuclear@0: out->mMaterialIndex = static_cast( conv_data.materials_raw.size() ); nuclear@0: conv_data.materials_raw.push_back(mat); nuclear@0: } nuclear@0: } nuclear@0: else out->mMaterialIndex = static_cast( -1 ); nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totface; ++i) { nuclear@0: nuclear@0: const MFace& mf = mesh->mface[i]; nuclear@0: nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ]; nuclear@0: aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: f.mIndices = new unsigned int[ f.mNumIndices = mf.v4?4:3 ]; nuclear@0: aiVector3D* vo = out->mVertices + out->mNumVertices; nuclear@0: aiVector3D* vn = out->mNormals + out->mNumVertices; nuclear@0: nuclear@0: // XXX we can't fold this easily, because we are restricted nuclear@0: // to the member names from the BLEND file (v1,v2,v3,v4) nuclear@0: // which are assigned by the genblenddna.py script and nuclear@0: // cannot be changed without breaking the entire nuclear@0: // import process. nuclear@0: nuclear@0: if (mf.v1 >= mesh->totvert) { nuclear@0: ThrowException("Vertex index v1 out of range"); nuclear@0: } nuclear@0: const MVert* v = &mesh->mvert[mf.v1]; nuclear@0: vo->x = v->co[0]; nuclear@0: vo->y = v->co[1]; nuclear@0: vo->z = v->co[2]; nuclear@0: vn->x = v->no[0]; nuclear@0: vn->y = v->no[1]; nuclear@0: vn->z = v->no[2]; nuclear@0: f.mIndices[0] = out->mNumVertices++; nuclear@0: ++vo; nuclear@0: ++vn; nuclear@0: nuclear@0: // if (f.mNumIndices >= 2) { nuclear@0: if (mf.v2 >= mesh->totvert) { nuclear@0: ThrowException("Vertex index v2 out of range"); nuclear@0: } nuclear@0: v = &mesh->mvert[mf.v2]; nuclear@0: vo->x = v->co[0]; nuclear@0: vo->y = v->co[1]; nuclear@0: vo->z = v->co[2]; nuclear@0: vn->x = v->no[0]; nuclear@0: vn->y = v->no[1]; nuclear@0: vn->z = v->no[2]; nuclear@0: f.mIndices[1] = out->mNumVertices++; nuclear@0: ++vo; nuclear@0: ++vn; nuclear@0: nuclear@0: if (mf.v3 >= mesh->totvert) { nuclear@0: ThrowException("Vertex index v3 out of range"); nuclear@0: } nuclear@0: // if (f.mNumIndices >= 3) { nuclear@0: v = &mesh->mvert[mf.v3]; nuclear@0: vo->x = v->co[0]; nuclear@0: vo->y = v->co[1]; nuclear@0: vo->z = v->co[2]; nuclear@0: vn->x = v->no[0]; nuclear@0: vn->y = v->no[1]; nuclear@0: vn->z = v->no[2]; nuclear@0: f.mIndices[2] = out->mNumVertices++; nuclear@0: ++vo; nuclear@0: ++vn; nuclear@0: nuclear@0: if (mf.v4 >= mesh->totvert) { nuclear@0: ThrowException("Vertex index v4 out of range"); nuclear@0: } nuclear@0: // if (f.mNumIndices >= 4) { nuclear@0: if (mf.v4) { nuclear@0: v = &mesh->mvert[mf.v4]; nuclear@0: vo->x = v->co[0]; nuclear@0: vo->y = v->co[1]; nuclear@0: vo->z = v->co[2]; nuclear@0: vn->x = v->no[0]; nuclear@0: vn->y = v->no[1]; nuclear@0: vn->z = v->no[2]; nuclear@0: f.mIndices[3] = out->mNumVertices++; nuclear@0: ++vo; nuclear@0: ++vn; nuclear@0: nuclear@0: out->mPrimitiveTypes |= aiPrimitiveType_POLYGON; nuclear@0: } nuclear@0: else out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; nuclear@0: nuclear@0: // } nuclear@0: // } nuclear@0: // } nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totpoly; ++i) { nuclear@0: nuclear@0: const MPoly& mf = mesh->mpoly[i]; nuclear@0: nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ]; nuclear@0: aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: f.mIndices = new unsigned int[ f.mNumIndices = mf.totloop ]; nuclear@0: aiVector3D* vo = out->mVertices + out->mNumVertices; nuclear@0: aiVector3D* vn = out->mNormals + out->mNumVertices; nuclear@0: nuclear@0: // XXX we can't fold this easily, because we are restricted nuclear@0: // to the member names from the BLEND file (v1,v2,v3,v4) nuclear@0: // which are assigned by the genblenddna.py script and nuclear@0: // cannot be changed without breaking the entire nuclear@0: // import process. nuclear@0: for (int j = 0;j < mf.totloop; ++j) nuclear@0: { nuclear@0: const MLoop& loop = mesh->mloop[mf.loopstart + j]; nuclear@0: nuclear@0: if (loop.v >= mesh->totvert) { nuclear@0: ThrowException("Vertex index out of range"); nuclear@0: } nuclear@0: nuclear@0: const MVert& v = mesh->mvert[loop.v]; nuclear@0: nuclear@0: vo->x = v.co[0]; nuclear@0: vo->y = v.co[1]; nuclear@0: vo->z = v.co[2]; nuclear@0: vn->x = v.no[0]; nuclear@0: vn->y = v.no[1]; nuclear@0: vn->z = v.no[2]; nuclear@0: f.mIndices[j] = out->mNumVertices++; nuclear@0: nuclear@0: ++vo; nuclear@0: ++vn; nuclear@0: nuclear@0: } nuclear@0: if (mf.totloop == 3) nuclear@0: { nuclear@0: out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: out->mPrimitiveTypes |= aiPrimitiveType_POLYGON; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // collect texture coordinates, they're stored in a separate per-face buffer nuclear@0: if (mesh->mtface || mesh->mloopuv) { nuclear@0: if (mesh->totface > static_cast ( mesh->mtface.size())) { nuclear@0: ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)"); nuclear@0: } nuclear@0: for (std::vector::iterator it = temp->begin()+old; it != temp->end(); ++it) { nuclear@0: ai_assert((*it)->mNumVertices && (*it)->mNumFaces); nuclear@0: nuclear@0: (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices]; nuclear@0: (*it)->mNumFaces = (*it)->mNumVertices = 0; nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totface; ++i) { nuclear@0: const MTFace* v = &mesh->mtface[i]; nuclear@0: nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ]; nuclear@0: const aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices]; nuclear@0: for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) { nuclear@0: vo->x = v->uv[i][0]; nuclear@0: vo->y = v->uv[i][1]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totpoly; ++i) { nuclear@0: const MPoly& v = mesh->mpoly[i]; nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ]; nuclear@0: const aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices]; nuclear@0: for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) { nuclear@0: const MLoopUV& uv = mesh->mloopuv[v.loopstart + j]; nuclear@0: vo->x = uv.uv[0]; nuclear@0: vo->y = uv.uv[1]; nuclear@0: } nuclear@0: nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // collect texture coordinates, old-style (marked as deprecated in current blender sources) nuclear@0: if (mesh->tface) { nuclear@0: if (mesh->totface > static_cast ( mesh->tface.size())) { nuclear@0: ThrowException("Number of faces is larger than the corresponding UV face array (#2)"); nuclear@0: } nuclear@0: for (std::vector::iterator it = temp->begin()+old; it != temp->end(); ++it) { nuclear@0: ai_assert((*it)->mNumVertices && (*it)->mNumFaces); nuclear@0: nuclear@0: (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices]; nuclear@0: (*it)->mNumFaces = (*it)->mNumVertices = 0; nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totface; ++i) { nuclear@0: const TFace* v = &mesh->tface[i]; nuclear@0: nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ]; nuclear@0: const aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices]; nuclear@0: for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) { nuclear@0: vo->x = v->uv[i][0]; nuclear@0: vo->y = v->uv[i][1]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // collect vertex colors, stored separately as well nuclear@0: if (mesh->mcol || mesh->mloopcol) { nuclear@0: if (mesh->totface > static_cast ( (mesh->mcol.size()/4)) ) { nuclear@0: ThrowException("Number of faces is larger than the corresponding color face array"); nuclear@0: } nuclear@0: for (std::vector::iterator it = temp->begin()+old; it != temp->end(); ++it) { nuclear@0: ai_assert((*it)->mNumVertices && (*it)->mNumFaces); nuclear@0: nuclear@0: (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices]; nuclear@0: (*it)->mNumFaces = (*it)->mNumVertices = 0; nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totface; ++i) { nuclear@0: nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ]; nuclear@0: const aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: aiColor4D* vo = &out->mColors[0][out->mNumVertices]; nuclear@0: for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo,++out->mNumVertices) { nuclear@0: const MCol* col = &mesh->mcol[(i<<2)+n]; nuclear@0: nuclear@0: vo->r = col->r; nuclear@0: vo->g = col->g; nuclear@0: vo->b = col->b; nuclear@0: vo->a = col->a; nuclear@0: } nuclear@0: for (unsigned int n = f.mNumIndices; n < 4; ++n); nuclear@0: } nuclear@0: nuclear@0: for (int i = 0; i < mesh->totpoly; ++i) { nuclear@0: const MPoly& v = mesh->mpoly[i]; nuclear@0: aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ]; nuclear@0: const aiFace& f = out->mFaces[out->mNumFaces++]; nuclear@0: nuclear@0: aiColor4D* vo = &out->mColors[0][out->mNumVertices]; nuclear@0: for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) { nuclear@0: const MLoopCol& col = mesh->mloopcol[v.loopstart + j]; nuclear@0: vo->r = col.r; nuclear@0: vo->g = col.g; nuclear@0: vo->b = col.b; nuclear@0: vo->a = col.a; nuclear@0: } nuclear@0: nuclear@0: } nuclear@0: nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* /*obj*/, const Camera* /*mesh*/, ConversionData& /*conv_data*/) nuclear@0: { nuclear@0: ScopeGuard out(new aiCamera()); nuclear@0: nuclear@0: return NULL ; //out.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* /*obj*/, const Lamp* /*mesh*/, ConversionData& /*conv_data*/) nuclear@0: { nuclear@0: ScopeGuard out(new aiLight()); nuclear@0: nuclear@0: return NULL ; //out.dismiss(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform) nuclear@0: { nuclear@0: std::deque children; nuclear@0: for(std::set::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) { nuclear@0: const Object* object = *it; nuclear@0: if (object->parent == obj) { nuclear@0: children.push_back(object); nuclear@0: nuclear@0: conv_data.objects.erase(it++); nuclear@0: continue; nuclear@0: } nuclear@0: ++it; nuclear@0: } nuclear@0: nuclear@0: ScopeGuard node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB' nuclear@0: if (obj->data) { nuclear@0: switch (obj->type) nuclear@0: { nuclear@0: case Object :: Type_EMPTY: nuclear@0: break; // do nothing nuclear@0: nuclear@0: nuclear@0: // supported object types nuclear@0: case Object :: Type_MESH: { nuclear@0: const size_t old = conv_data.meshes->size(); nuclear@0: nuclear@0: CheckActualType(obj->data.get(),"Mesh"); nuclear@0: ConvertMesh(in,obj,static_cast(obj->data.get()),conv_data,conv_data.meshes); nuclear@0: nuclear@0: if (conv_data.meshes->size() > old) { nuclear@0: node->mMeshes = new unsigned int[node->mNumMeshes = static_cast(conv_data.meshes->size()-old)]; nuclear@0: for (unsigned int i = 0; i < node->mNumMeshes; ++i) { nuclear@0: node->mMeshes[i] = i + old; nuclear@0: } nuclear@0: }} nuclear@0: break; nuclear@0: case Object :: Type_LAMP: { nuclear@0: CheckActualType(obj->data.get(),"Lamp"); nuclear@0: aiLight* mesh = ConvertLight(in,obj,static_cast( nuclear@0: obj->data.get()),conv_data); nuclear@0: nuclear@0: if (mesh) { nuclear@0: conv_data.lights->push_back(mesh); nuclear@0: }} nuclear@0: break; nuclear@0: case Object :: Type_CAMERA: { nuclear@0: CheckActualType(obj->data.get(),"Camera"); nuclear@0: aiCamera* mesh = ConvertCamera(in,obj,static_cast( nuclear@0: obj->data.get()),conv_data); nuclear@0: nuclear@0: if (mesh) { nuclear@0: conv_data.cameras->push_back(mesh); nuclear@0: }} nuclear@0: break; nuclear@0: nuclear@0: nuclear@0: // unsupported object types / log, but do not break nuclear@0: case Object :: Type_CURVE: nuclear@0: NotSupportedObjectType(obj,"Curve"); nuclear@0: break; nuclear@0: case Object :: Type_SURF: nuclear@0: NotSupportedObjectType(obj,"Surface"); nuclear@0: break; nuclear@0: case Object :: Type_FONT: nuclear@0: NotSupportedObjectType(obj,"Font"); nuclear@0: break; nuclear@0: case Object :: Type_MBALL: nuclear@0: NotSupportedObjectType(obj,"MetaBall"); nuclear@0: break; nuclear@0: case Object :: Type_WAVE: nuclear@0: NotSupportedObjectType(obj,"Wave"); nuclear@0: break; nuclear@0: case Object :: Type_LATTICE: nuclear@0: NotSupportedObjectType(obj,"Lattice"); nuclear@0: break; nuclear@0: nuclear@0: // invalid or unknown type nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: for(unsigned int x = 0; x < 4; ++x) { nuclear@0: for(unsigned int y = 0; y < 4; ++y) { nuclear@0: node->mTransformation[y][x] = obj->obmat[x][y]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: aiMatrix4x4 m = parentTransform; nuclear@0: m = m.Inverse(); nuclear@0: nuclear@0: node->mTransformation = m*node->mTransformation; nuclear@0: nuclear@0: if (children.size()) { nuclear@0: node->mNumChildren = static_cast(children.size()); nuclear@0: aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren](); nuclear@0: for_each (const Object* nobj,children) { nuclear@0: *nd = ConvertNode(in,nobj,conv_data,node->mTransformation * parentTransform); nuclear@0: (*nd++)->mParent = node; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // apply modifiers nuclear@0: modifier_cache->ApplyModifiers(*node,conv_data,in,*obj); nuclear@0: nuclear@0: return node.dismiss(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: #endif