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 BlenderDNA.cpp nuclear@0: * @brief Implementation of the Blender `DNA`, that is its own nuclear@0: * serialized set of data structures. nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER nuclear@0: #include "BlenderDNA.h" nuclear@0: #include "StreamReader.h" nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::Blender; nuclear@0: using namespace Assimp::Formatter; nuclear@0: nuclear@0: #define for_each BOOST_FOREACH nuclear@0: bool match4(StreamReaderAny& stream, const char* string) { nuclear@0: char tmp[] = { nuclear@0: (stream).GetI1(), nuclear@0: (stream).GetI1(), nuclear@0: (stream).GetI1(), nuclear@0: (stream).GetI1() nuclear@0: }; nuclear@0: return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); nuclear@0: } nuclear@0: nuclear@0: struct Type { nuclear@0: size_t size; nuclear@0: std::string name; nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void DNAParser :: Parse () nuclear@0: { nuclear@0: StreamReaderAny& stream = *db.reader.get(); nuclear@0: DNA& dna = db.dna; nuclear@0: nuclear@0: if(!match4(stream,"SDNA")) { nuclear@0: throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); nuclear@0: } nuclear@0: nuclear@0: // name dictionary nuclear@0: if(!match4(stream,"NAME")) { nuclear@0: throw DeadlyImportError("BlenderDNA: Expected NAME field"); nuclear@0: } nuclear@0: nuclear@0: std::vector names (stream.GetI4()); nuclear@0: for_each(std::string& s, names) { nuclear@0: while (char c = stream.GetI1()) { nuclear@0: s += c; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // type dictionary nuclear@0: for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); nuclear@0: if(!match4(stream,"TYPE")) { nuclear@0: throw DeadlyImportError("BlenderDNA: Expected TYPE field"); nuclear@0: } nuclear@0: nuclear@0: std::vector types (stream.GetI4()); nuclear@0: for_each(Type& s, types) { nuclear@0: while (char c = stream.GetI1()) { nuclear@0: s.name += c; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // type length dictionary nuclear@0: for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); nuclear@0: if(!match4(stream,"TLEN")) { nuclear@0: throw DeadlyImportError("BlenderDNA: Expected TLEN field"); nuclear@0: } nuclear@0: nuclear@0: for_each(Type& s, types) { nuclear@0: s.size = stream.GetI2(); nuclear@0: } nuclear@0: nuclear@0: // structures dictionary nuclear@0: for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); nuclear@0: if(!match4(stream,"STRC")) { nuclear@0: throw DeadlyImportError("BlenderDNA: Expected STRC field"); nuclear@0: } nuclear@0: nuclear@0: size_t end = stream.GetI4(), fields = 0; nuclear@0: nuclear@0: dna.structures.reserve(end); nuclear@0: for(size_t i = 0; i != end; ++i) { nuclear@0: nuclear@0: uint16_t n = stream.GetI2(); nuclear@0: if (n >= types.size()) { nuclear@0: throw DeadlyImportError((format(), nuclear@0: "BlenderDNA: Invalid type index in structure name" ,n, nuclear@0: " (there are only ", types.size(), " entries)" nuclear@0: )); nuclear@0: } nuclear@0: nuclear@0: // maintain separate indexes nuclear@0: dna.indices[types[n].name] = dna.structures.size(); nuclear@0: nuclear@0: dna.structures.push_back(Structure()); nuclear@0: Structure& s = dna.structures.back(); nuclear@0: s.name = types[n].name; nuclear@0: //s.index = dna.structures.size()-1; nuclear@0: nuclear@0: n = stream.GetI2(); nuclear@0: s.fields.reserve(n); nuclear@0: nuclear@0: size_t offset = 0; nuclear@0: for (size_t m = 0; m < n; ++m, ++fields) { nuclear@0: nuclear@0: uint16_t j = stream.GetI2(); nuclear@0: if (j >= types.size()) { nuclear@0: throw DeadlyImportError((format(), nuclear@0: "BlenderDNA: Invalid type index in structure field ", j, nuclear@0: " (there are only ", types.size(), " entries)" nuclear@0: )); nuclear@0: } nuclear@0: s.fields.push_back(Field()); nuclear@0: Field& f = s.fields.back(); nuclear@0: f.offset = offset; nuclear@0: nuclear@0: f.type = types[j].name; nuclear@0: f.size = types[j].size; nuclear@0: nuclear@0: j = stream.GetI2(); nuclear@0: if (j >= names.size()) { nuclear@0: throw DeadlyImportError((format(), nuclear@0: "BlenderDNA: Invalid name index in structure field ", j, nuclear@0: " (there are only ", names.size(), " entries)" nuclear@0: )); nuclear@0: } nuclear@0: nuclear@0: f.name = names[j]; nuclear@0: f.flags = 0u; nuclear@0: nuclear@0: // pointers always specify the size of the pointee instead of their own. nuclear@0: // The pointer asterisk remains a property of the lookup name. nuclear@0: if (f.name[0] == '*') { nuclear@0: f.size = db.i64bit ? 8 : 4; nuclear@0: f.flags |= FieldFlag_Pointer; nuclear@0: } nuclear@0: nuclear@0: // arrays, however, specify the size of a single element so we nuclear@0: // need to parse the (possibly multi-dimensional) array declaration nuclear@0: // in order to obtain the actual size of the array in the file. nuclear@0: // Also we need to alter the lookup name to include no array nuclear@0: // brackets anymore or size fixup won't work (if our size does nuclear@0: // not match the size read from the DNA). nuclear@0: if (*f.name.rbegin() == ']') { nuclear@0: const std::string::size_type rb = f.name.find('['); nuclear@0: if (rb == std::string::npos) { nuclear@0: throw DeadlyImportError((format(), nuclear@0: "BlenderDNA: Encountered invalid array declaration ", nuclear@0: f.name nuclear@0: )); nuclear@0: } nuclear@0: nuclear@0: f.flags |= FieldFlag_Array; nuclear@0: DNA::ExtractArraySize(f.name,f.array_sizes); nuclear@0: f.name = f.name.substr(0,rb); nuclear@0: nuclear@0: f.size *= f.array_sizes[0] * f.array_sizes[1]; nuclear@0: } nuclear@0: nuclear@0: // maintain separate indexes nuclear@0: s.indices[f.name] = s.fields.size()-1; nuclear@0: offset += f.size; nuclear@0: } nuclear@0: s.size = offset; nuclear@0: } nuclear@0: nuclear@0: DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), nuclear@0: " structures with totally ",fields," fields")); nuclear@0: nuclear@0: #ifdef ASSIMP_BUILD_BLENDER_DEBUG nuclear@0: dna.DumpToFile(); nuclear@0: #endif nuclear@0: nuclear@0: dna.AddPrimitiveStructures(); nuclear@0: dna.RegisterConverters(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: #ifdef ASSIMP_BUILD_BLENDER_DEBUG nuclear@0: nuclear@0: #include nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void DNA :: DumpToFile() nuclear@0: { nuclear@0: // we dont't bother using the VFS here for this is only for debugging. nuclear@0: // (and all your bases are belong to us). nuclear@0: nuclear@0: std::ofstream f("dna.txt"); nuclear@0: if (f.fail()) { nuclear@0: DefaultLogger::get()->error("Could not dump dna to dna.txt"); nuclear@0: return; nuclear@0: } nuclear@0: f << "Field format: type name offset size" << "\n"; nuclear@0: f << "Structure format: name size" << "\n"; nuclear@0: nuclear@0: for_each(const Structure& s, structures) { nuclear@0: f << s.name << " " << s.size << "\n\n"; nuclear@0: for_each(const Field& ff, s.fields) { nuclear@0: f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl; nuclear@0: } nuclear@0: f << std::endl; nuclear@0: } nuclear@0: DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: /*static*/ void DNA :: ExtractArraySize( nuclear@0: const std::string& out, nuclear@0: size_t array_sizes[2] nuclear@0: ) nuclear@0: { nuclear@0: array_sizes[0] = array_sizes[1] = 1; nuclear@0: std::string::size_type pos = out.find('['); nuclear@0: if (pos++ == std::string::npos) { nuclear@0: return; nuclear@0: } nuclear@0: array_sizes[0] = strtoul10(&out[pos]); nuclear@0: nuclear@0: pos = out.find('[',pos); nuclear@0: if (pos++ == std::string::npos) { nuclear@0: return; nuclear@0: } nuclear@0: array_sizes[1] = strtoul10(&out[pos]); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( nuclear@0: const Structure& structure, nuclear@0: const FileDatabase& db nuclear@0: ) const nuclear@0: { nuclear@0: std::map::const_iterator it = converters.find(structure.name); nuclear@0: if (it == converters.end()) { nuclear@0: return boost::shared_ptr< ElemBase >(); nuclear@0: } nuclear@0: nuclear@0: boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); nuclear@0: (structure.*((*it).second.second))(ret,db); nuclear@0: nuclear@0: return ret; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: DNA::FactoryPair DNA :: GetBlobToStructureConverter( nuclear@0: const Structure& structure, nuclear@0: const FileDatabase& /*db*/ nuclear@0: ) const nuclear@0: { nuclear@0: std::map::const_iterator it = converters.find(structure.name); nuclear@0: return it == converters.end() ? FactoryPair() : (*it).second; nuclear@0: } nuclear@0: nuclear@0: // basing on http://www.blender.org/development/architecture/notes-on-sdna/ nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void DNA :: AddPrimitiveStructures() nuclear@0: { nuclear@0: // NOTE: these are just dummies. Their presence enforces nuclear@0: // Structure::Convert to be called on these nuclear@0: // empty structures. These converters are special nuclear@0: // overloads which scan the name of the structure and nuclear@0: // perform the required data type conversion if one nuclear@0: // of these special names is found in the structure nuclear@0: // in question. nuclear@0: nuclear@0: indices["int"] = structures.size(); nuclear@0: structures.push_back( Structure() ); nuclear@0: structures.back().name = "int"; nuclear@0: structures.back().size = 4; nuclear@0: nuclear@0: indices["short"] = structures.size(); nuclear@0: structures.push_back( Structure() ); nuclear@0: structures.back().name = "short"; nuclear@0: structures.back().size = 2; nuclear@0: nuclear@0: nuclear@0: indices["char"] = structures.size(); nuclear@0: structures.push_back( Structure() ); nuclear@0: structures.back().name = "char"; nuclear@0: structures.back().size = 1; nuclear@0: nuclear@0: nuclear@0: indices["float"] = structures.size(); nuclear@0: structures.push_back( Structure() ); nuclear@0: structures.back().name = "float"; nuclear@0: structures.back().size = 4; nuclear@0: nuclear@0: nuclear@0: indices["double"] = structures.size(); nuclear@0: structures.push_back( Structure() ); nuclear@0: structures.back().name = "double"; nuclear@0: structures.back().size = 8; nuclear@0: nuclear@0: // no long, seemingly. nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void SectionParser :: Next() nuclear@0: { nuclear@0: stream.SetCurrentPos(current.start + current.size); nuclear@0: nuclear@0: const char tmp[] = { nuclear@0: stream.GetI1(), nuclear@0: stream.GetI1(), nuclear@0: stream.GetI1(), nuclear@0: stream.GetI1() nuclear@0: }; nuclear@0: current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); nuclear@0: nuclear@0: current.size = stream.GetI4(); nuclear@0: current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); nuclear@0: nuclear@0: current.dna_index = stream.GetI4(); nuclear@0: current.num = stream.GetI4(); nuclear@0: nuclear@0: current.start = stream.GetCurrentPos(); nuclear@0: if (stream.GetRemainingSizeToLimit() < current.size) { nuclear@0: throw DeadlyImportError("BLEND: invalid size of file block"); nuclear@0: } nuclear@0: nuclear@0: #ifdef ASSIMP_BUILD_BLENDER_DEBUG nuclear@0: DefaultLogger::get()->debug(current.id); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: #endif