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 FBXParser.cpp nuclear@0: * @brief Implementation of the FBX parser and the rudimentary DOM that we use nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER nuclear@0: nuclear@0: 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: nuclear@0: nuclear@0: #include "FBXTokenizer.h" nuclear@0: #include "FBXParser.h" nuclear@0: #include "FBXUtil.h" nuclear@0: nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: using namespace Assimp::FBX; nuclear@0: nuclear@0: namespace { nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // signal parse error, this is always unrecoverable. Throws DeadlyImportError. nuclear@0: void ParseError(const std::string& message, const Token& token) nuclear@0: { nuclear@0: throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token)); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ParseError(const std::string& message, const Element* element = NULL) nuclear@0: { nuclear@0: if(element) { nuclear@0: ParseError(message,element->KeyToken()); nuclear@0: } nuclear@0: throw DeadlyImportError("FBX-Parser " + message); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // print warning, do return nuclear@0: void ParseWarning(const std::string& message, const Token& token) nuclear@0: { nuclear@0: if(DefaultLogger::get()) { nuclear@0: DefaultLogger::get()->warn(Util::AddTokenText("FBX-Parser",message,&token)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ParseWarning(const std::string& message, const Element* element = NULL) nuclear@0: { nuclear@0: if(element) { nuclear@0: ParseWarning(message,element->KeyToken()); nuclear@0: return; nuclear@0: } nuclear@0: if(DefaultLogger::get()) { nuclear@0: DefaultLogger::get()->warn("FBX-Parser: " + message); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void ParseError(const std::string& message, TokenPtr token) nuclear@0: { nuclear@0: if(token) { nuclear@0: ParseError(message, *token); nuclear@0: } nuclear@0: ParseError(message); nuclear@0: } nuclear@0: nuclear@0: } nuclear@0: nuclear@0: namespace Assimp { nuclear@0: namespace FBX { nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Element::Element(const Token& key_token, Parser& parser) nuclear@0: : key_token(key_token) nuclear@0: { nuclear@0: TokenPtr n = NULL; nuclear@0: do { nuclear@0: n = parser.AdvanceToNextToken(); nuclear@0: if(!n) { nuclear@0: ParseError("unexpected end of file, expected closing bracket",parser.LastToken()); nuclear@0: } nuclear@0: nuclear@0: if (n->Type() == TokenType_DATA) { nuclear@0: tokens.push_back(n); nuclear@0: nuclear@0: n = parser.AdvanceToNextToken(); nuclear@0: if(!n) { nuclear@0: ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken()); nuclear@0: } nuclear@0: nuclear@0: const TokenType ty = n->Type(); nuclear@0: if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) { nuclear@0: ParseError("unexpected token; expected bracket, comma or key",n); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (n->Type() == TokenType_OPEN_BRACKET) { nuclear@0: compound.reset(new Scope(parser)); nuclear@0: nuclear@0: // current token should be a TOK_CLOSE_BRACKET nuclear@0: n = parser.CurrentToken(); nuclear@0: ai_assert(n); nuclear@0: nuclear@0: if (n->Type() != TokenType_CLOSE_BRACKET) { nuclear@0: ParseError("expected closing bracket",n); nuclear@0: } nuclear@0: nuclear@0: parser.AdvanceToNextToken(); nuclear@0: return; nuclear@0: } nuclear@0: } nuclear@0: while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Element::~Element() nuclear@0: { nuclear@0: // no need to delete tokens, they are owned by the parser nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Scope::Scope(Parser& parser,bool topLevel) nuclear@0: { nuclear@0: if(!topLevel) { nuclear@0: TokenPtr t = parser.CurrentToken(); nuclear@0: if (t->Type() != TokenType_OPEN_BRACKET) { nuclear@0: ParseError("expected open bracket",t); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: TokenPtr n = parser.AdvanceToNextToken(); nuclear@0: if(n == NULL) { nuclear@0: ParseError("unexpected end of file"); nuclear@0: } nuclear@0: nuclear@0: // note: empty scopes are allowed nuclear@0: while(n->Type() != TokenType_CLOSE_BRACKET) { nuclear@0: if (n->Type() != TokenType_KEY) { nuclear@0: ParseError("unexpected token, expected TOK_KEY",n); nuclear@0: } nuclear@0: nuclear@0: const std::string& str = n->StringContents(); nuclear@0: elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); nuclear@0: nuclear@0: // Element() should stop at the next Key token (or right after a Close token) nuclear@0: n = parser.CurrentToken(); nuclear@0: if(n == NULL) { nuclear@0: if (topLevel) { nuclear@0: return; nuclear@0: } nuclear@0: ParseError("unexpected end of file",parser.LastToken()); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Scope::~Scope() nuclear@0: { nuclear@0: BOOST_FOREACH(ElementMap::value_type& v, elements) { nuclear@0: delete v.second; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Parser::Parser (const TokenList& tokens, bool is_binary) nuclear@0: : tokens(tokens) nuclear@0: , last() nuclear@0: , current() nuclear@0: , cursor(tokens.begin()) nuclear@0: , is_binary(is_binary) nuclear@0: { nuclear@0: root.reset(new Scope(*this,true)); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Parser::~Parser() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: TokenPtr Parser::AdvanceToNextToken() nuclear@0: { nuclear@0: last = current; nuclear@0: if (cursor == tokens.end()) { nuclear@0: current = NULL; nuclear@0: } nuclear@0: else { nuclear@0: current = *cursor++; nuclear@0: } nuclear@0: return current; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: TokenPtr Parser::CurrentToken() const nuclear@0: { nuclear@0: return current; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: TokenPtr Parser::LastToken() const nuclear@0: { nuclear@0: return last; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: uint64_t ParseTokenAsID(const Token& t, const char*& err_out) nuclear@0: { nuclear@0: err_out = NULL; nuclear@0: nuclear@0: if (t.Type() != TokenType_DATA) { nuclear@0: err_out = "expected TOK_DATA token"; nuclear@0: return 0L; nuclear@0: } nuclear@0: nuclear@0: if(t.IsBinary()) nuclear@0: { nuclear@0: const char* data = t.begin(); nuclear@0: if (data[0] != 'L') { nuclear@0: err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; nuclear@0: return 0L; nuclear@0: } nuclear@0: nuclear@0: ai_assert(t.end() - data == 9); nuclear@0: nuclear@0: BE_NCONST uint64_t id = *reinterpret_cast(data+1); nuclear@0: AI_SWAP8(id); nuclear@0: return id; nuclear@0: } nuclear@0: nuclear@0: // XXX: should use size_t here nuclear@0: unsigned int length = static_cast(t.end() - t.begin()); nuclear@0: ai_assert(length > 0); nuclear@0: nuclear@0: const char* out; nuclear@0: const uint64_t id = strtoul10_64(t.begin(),&out,&length); nuclear@0: if (out > t.end()) { nuclear@0: err_out = "failed to parse ID (text)"; nuclear@0: return 0L; nuclear@0: } nuclear@0: nuclear@0: return id; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: size_t ParseTokenAsDim(const Token& t, const char*& err_out) nuclear@0: { nuclear@0: // same as ID parsing, except there is a trailing asterisk nuclear@0: err_out = NULL; nuclear@0: nuclear@0: if (t.Type() != TokenType_DATA) { nuclear@0: err_out = "expected TOK_DATA token"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(t.IsBinary()) nuclear@0: { nuclear@0: const char* data = t.begin(); nuclear@0: if (data[0] != 'L') { nuclear@0: err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: ai_assert(t.end() - data == 9); nuclear@0: BE_NCONST uint64_t id = *reinterpret_cast(data+1); nuclear@0: AI_SWAP8(id); nuclear@0: return static_cast(id); nuclear@0: } nuclear@0: nuclear@0: if(*t.begin() != '*') { nuclear@0: err_out = "expected asterisk before array dimension"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: // XXX: should use size_t here nuclear@0: unsigned int length = static_cast(t.end() - t.begin()); nuclear@0: if(length == 0) { nuclear@0: err_out = "expected valid integer number after asterisk"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: const char* out; nuclear@0: const size_t id = static_cast(strtoul10_64(t.begin() + 1,&out,&length)); nuclear@0: if (out > t.end()) { nuclear@0: err_out = "failed to parse ID"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: return id; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: float ParseTokenAsFloat(const Token& t, const char*& err_out) nuclear@0: { nuclear@0: err_out = NULL; nuclear@0: nuclear@0: if (t.Type() != TokenType_DATA) { nuclear@0: err_out = "expected TOK_DATA token"; nuclear@0: return 0.0f; nuclear@0: } nuclear@0: nuclear@0: if(t.IsBinary()) nuclear@0: { nuclear@0: const char* data = t.begin(); nuclear@0: if (data[0] != 'F' && data[0] != 'D') { nuclear@0: err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)"; nuclear@0: return 0.0f; nuclear@0: } nuclear@0: nuclear@0: if (data[0] == 'F') { nuclear@0: ai_assert(t.end() - data == 5); nuclear@0: // no byte swapping needed for ieee floats nuclear@0: float res; nuclear@0: memcpy(&res, data + 1, sizeof res); nuclear@0: return res; nuclear@0: } nuclear@0: else { nuclear@0: ai_assert(t.end() - data == 9); nuclear@0: // no byte swapping needed for ieee floats nuclear@0: double res; nuclear@0: memcpy(&res, data + 1, sizeof res); nuclear@0: return (float)res; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // need to copy the input string to a temporary buffer nuclear@0: // first - next in the fbx token stream comes ',', nuclear@0: // which fast_atof could interpret as decimal point. nuclear@0: #define MAX_FLOAT_LENGTH 31 nuclear@0: char temp[MAX_FLOAT_LENGTH + 1]; nuclear@0: const size_t length = static_cast(t.end()-t.begin()); nuclear@0: std::copy(t.begin(),t.end(),temp); nuclear@0: temp[std::min(static_cast(MAX_FLOAT_LENGTH),length)] = '\0'; nuclear@0: nuclear@0: return fast_atof(temp); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: int ParseTokenAsInt(const Token& t, const char*& err_out) nuclear@0: { nuclear@0: err_out = NULL; nuclear@0: nuclear@0: if (t.Type() != TokenType_DATA) { nuclear@0: err_out = "expected TOK_DATA token"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(t.IsBinary()) nuclear@0: { nuclear@0: const char* data = t.begin(); nuclear@0: if (data[0] != 'I') { nuclear@0: err_out = "failed to parse I(nt), unexpected data type (binary)"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: ai_assert(t.end() - data == 5); nuclear@0: BE_NCONST int32_t ival = *reinterpret_cast(data+1); nuclear@0: AI_SWAP4(ival); nuclear@0: return static_cast(ival); nuclear@0: } nuclear@0: nuclear@0: ai_assert(static_cast(t.end() - t.begin()) > 0); nuclear@0: nuclear@0: const char* out; nuclear@0: const int intval = strtol10(t.begin(),&out); nuclear@0: if (out != t.end()) { nuclear@0: err_out = "failed to parse ID"; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: return intval; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::string ParseTokenAsString(const Token& t, const char*& err_out) nuclear@0: { nuclear@0: err_out = NULL; nuclear@0: nuclear@0: if (t.Type() != TokenType_DATA) { nuclear@0: err_out = "expected TOK_DATA token"; nuclear@0: return ""; nuclear@0: } nuclear@0: nuclear@0: if(t.IsBinary()) nuclear@0: { nuclear@0: const char* data = t.begin(); nuclear@0: if (data[0] != 'S') { nuclear@0: err_out = "failed to parse S(tring), unexpected data type (binary)"; nuclear@0: return ""; nuclear@0: } nuclear@0: nuclear@0: ai_assert(t.end() - data >= 5); nuclear@0: nuclear@0: // read string length nuclear@0: BE_NCONST int32_t len = *reinterpret_cast(data+1); nuclear@0: AI_SWAP4(len); nuclear@0: nuclear@0: ai_assert(t.end() - data == 5 + len); nuclear@0: return std::string(data + 5, len); nuclear@0: } nuclear@0: nuclear@0: const size_t length = static_cast(t.end() - t.begin()); nuclear@0: if(length < 2) { nuclear@0: err_out = "token is too short to hold a string"; nuclear@0: return ""; nuclear@0: } nuclear@0: nuclear@0: const char* s = t.begin(), *e = t.end() - 1; nuclear@0: if (*s != '\"' || *e != '\"') { nuclear@0: err_out = "expected double quoted string"; nuclear@0: return ""; nuclear@0: } nuclear@0: nuclear@0: return std::string(s+1,length-2); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: namespace { nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read the type code and element count of a binary data array and stop there nuclear@0: void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, nuclear@0: const Element& el) nuclear@0: { nuclear@0: if (static_cast(end-data) < 5) { nuclear@0: ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el); nuclear@0: } nuclear@0: nuclear@0: // data type nuclear@0: type = *data; nuclear@0: nuclear@0: // read number of elements nuclear@0: BE_NCONST uint32_t len = *reinterpret_cast(data+1); nuclear@0: AI_SWAP4(len); nuclear@0: nuclear@0: count = len; nuclear@0: data += 5; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header) nuclear@0: void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, nuclear@0: std::vector& buff, nuclear@0: const Element& el) nuclear@0: { nuclear@0: ai_assert(static_cast(end-data) >= 4); // runtime check for this happens at tokenization stage nuclear@0: nuclear@0: BE_NCONST uint32_t encmode = *reinterpret_cast(data); nuclear@0: AI_SWAP4(encmode); nuclear@0: data += 4; nuclear@0: nuclear@0: // next comes the compressed length nuclear@0: BE_NCONST uint32_t comp_len = *reinterpret_cast(data); nuclear@0: AI_SWAP4(comp_len); nuclear@0: data += 4; nuclear@0: nuclear@0: ai_assert(data + comp_len == end); nuclear@0: nuclear@0: // determine the length of the uncompressed data by looking at the type signature nuclear@0: uint32_t stride; nuclear@0: switch(type) nuclear@0: { nuclear@0: case 'f': nuclear@0: case 'i': nuclear@0: stride = 4; nuclear@0: break; nuclear@0: nuclear@0: case 'd': nuclear@0: case 'l': nuclear@0: stride = 8; nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: ai_assert(false); nuclear@0: }; nuclear@0: nuclear@0: const uint32_t full_length = stride * count; nuclear@0: buff.resize(full_length); nuclear@0: nuclear@0: if(encmode == 0) { nuclear@0: ai_assert(full_length == comp_len); nuclear@0: nuclear@0: // plain data, no compression nuclear@0: std::copy(data, end, buff.begin()); nuclear@0: } nuclear@0: else if(encmode == 1) { nuclear@0: // zlib/deflate, next comes ZIP head (0x78 0x01) nuclear@0: // see http://www.ietf.org/rfc/rfc1950.txt nuclear@0: 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: inflateInit(&zstream); nuclear@0: nuclear@0: zstream.next_in = reinterpret_cast( const_cast(data) ); nuclear@0: zstream.avail_in = comp_len; nuclear@0: nuclear@0: zstream.avail_out = buff.size(); nuclear@0: zstream.next_out = reinterpret_cast(&*buff.begin()); nuclear@0: const int ret = inflate(&zstream, Z_FINISH); nuclear@0: nuclear@0: if (ret != Z_STREAM_END && ret != Z_OK) { nuclear@0: ParseError("failure decompressing compressed data section"); nuclear@0: } nuclear@0: nuclear@0: // terminate zlib nuclear@0: inflateEnd(&zstream); nuclear@0: } nuclear@0: #ifdef _DEBUG nuclear@0: else { nuclear@0: // runtime check for this happens at tokenization stage nuclear@0: ai_assert(false); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: data += comp_len; nuclear@0: ai_assert(data == end); nuclear@0: } nuclear@0: nuclear@0: } // !anon nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of float3 tuples nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(count % 3 != 0) { nuclear@0: ParseError("number of floats is not a multiple of three (3) (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'd' && type != 'f') { nuclear@0: ParseError("expected float or double array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); nuclear@0: nuclear@0: const uint32_t count3 = count / 3; nuclear@0: out.reserve(count3); nuclear@0: nuclear@0: if (type == 'd') { nuclear@0: const double* d = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count3; ++i, d += 3) { nuclear@0: out.push_back(aiVector3D(static_cast(d[0]), nuclear@0: static_cast(d[1]), nuclear@0: static_cast(d[2]))); nuclear@0: } nuclear@0: } nuclear@0: else if (type == 'f') { nuclear@0: const float* f = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count3; ++i, f += 3) { nuclear@0: out.push_back(aiVector3D(f[0],f[1],f[2])); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // may throw bad_alloc if the input is rubbish, but this need nuclear@0: // not to be prevented - importing would fail but we wouldn't nuclear@0: // crash since assimp handles this case properly. nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: if (a.Tokens().size() % 3 != 0) { nuclear@0: ParseError("number of floats is not a multiple of three (3)",&el); nuclear@0: } nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: aiVector3D v; nuclear@0: v.x = ParseTokenAsFloat(**it++); nuclear@0: v.y = ParseTokenAsFloat(**it++); nuclear@0: v.z = ParseTokenAsFloat(**it++); nuclear@0: nuclear@0: out.push_back(v); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of color4 tuples nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(count % 4 != 0) { nuclear@0: ParseError("number of floats is not a multiple of four (4) (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'd' && type != 'f') { nuclear@0: ParseError("expected float or double array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); nuclear@0: nuclear@0: const uint32_t count4 = count / 4; nuclear@0: out.reserve(count4); nuclear@0: nuclear@0: if (type == 'd') { nuclear@0: const double* d = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count4; ++i, d += 4) { nuclear@0: out.push_back(aiColor4D(static_cast(d[0]), nuclear@0: static_cast(d[1]), nuclear@0: static_cast(d[2]), nuclear@0: static_cast(d[3]))); nuclear@0: } nuclear@0: } nuclear@0: else if (type == 'f') { nuclear@0: const float* f = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count4; ++i, f += 4) { nuclear@0: out.push_back(aiColor4D(f[0],f[1],f[2],f[3])); nuclear@0: } nuclear@0: } nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() above nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: if (a.Tokens().size() % 4 != 0) { nuclear@0: ParseError("number of floats is not a multiple of four (4)",&el); nuclear@0: } nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: aiColor4D v; nuclear@0: v.r = ParseTokenAsFloat(**it++); nuclear@0: v.g = ParseTokenAsFloat(**it++); nuclear@0: v.b = ParseTokenAsFloat(**it++); nuclear@0: v.a = ParseTokenAsFloat(**it++); nuclear@0: nuclear@0: out.push_back(v); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of float2 tuples nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(count % 2 != 0) { nuclear@0: ParseError("number of floats is not a multiple of two (2) (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'd' && type != 'f') { nuclear@0: ParseError("expected float or double array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); nuclear@0: nuclear@0: const uint32_t count2 = count / 2; nuclear@0: out.reserve(count2); nuclear@0: nuclear@0: if (type == 'd') { nuclear@0: const double* d = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count2; ++i, d += 2) { nuclear@0: out.push_back(aiVector2D(static_cast(d[0]), nuclear@0: static_cast(d[1]))); nuclear@0: } nuclear@0: } nuclear@0: else if (type == 'f') { nuclear@0: const float* f = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count2; ++i, f += 2) { nuclear@0: out.push_back(aiVector2D(f[0],f[1])); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() above nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: if (a.Tokens().size() % 2 != 0) { nuclear@0: ParseError("number of floats is not a multiple of two (2)",&el); nuclear@0: } nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: aiVector2D v; nuclear@0: v.x = ParseTokenAsFloat(**it++); nuclear@0: v.y = ParseTokenAsFloat(**it++); nuclear@0: nuclear@0: out.push_back(v); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of ints nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'i') { nuclear@0: ParseError("expected int array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * 4); nuclear@0: nuclear@0: out.reserve(count); nuclear@0: nuclear@0: const int32_t* ip = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count; ++i, ++ip) { nuclear@0: BE_NCONST int32_t val = *ip; nuclear@0: AI_SWAP4(val); nuclear@0: out.push_back(val); nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: const int ival = ParseTokenAsInt(**it++); nuclear@0: out.push_back(ival); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of floats nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'd' && type != 'f') { nuclear@0: ParseError("expected float or double array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); nuclear@0: nuclear@0: if (type == 'd') { nuclear@0: const double* d = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count; ++i, ++d) { nuclear@0: out.push_back(static_cast(*d)); nuclear@0: } nuclear@0: } nuclear@0: else if (type == 'f') { nuclear@0: const float* f = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count; ++i, ++f) { nuclear@0: out.push_back(*f); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: const float ival = ParseTokenAsFloat(**it++); nuclear@0: out.push_back(ival); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of uints nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'i') { nuclear@0: ParseError("expected (u)int array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * 4); nuclear@0: nuclear@0: out.reserve(count); nuclear@0: nuclear@0: const int32_t* ip = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count; ++i, ++ip) { nuclear@0: BE_NCONST int32_t val = *ip; nuclear@0: if(val < 0) { nuclear@0: ParseError("encountered negative integer index (binary)"); nuclear@0: } nuclear@0: nuclear@0: AI_SWAP4(val); nuclear@0: out.push_back(val); nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: const int ival = ParseTokenAsInt(**it++); nuclear@0: if(ival < 0) { nuclear@0: ParseError("encountered negative integer index"); nuclear@0: } nuclear@0: out.push_back(static_cast(ival)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read an array of uint64_ts nuclear@0: void ParseVectorDataArray(std::vector& out, const Element& el) nuclear@0: { nuclear@0: out.clear(); nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: ParseError("unexpected empty element",&el); nuclear@0: } nuclear@0: nuclear@0: if(tok[0]->IsBinary()) { nuclear@0: const char* data = tok[0]->begin(), *end = tok[0]->end(); nuclear@0: nuclear@0: char type; nuclear@0: uint32_t count; nuclear@0: ReadBinaryDataArrayHead(data, end, type, count, el); nuclear@0: nuclear@0: if(!count) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (type != 'l') { nuclear@0: ParseError("expected long array (binary)",&el); nuclear@0: } nuclear@0: nuclear@0: std::vector buff; nuclear@0: ReadBinaryDataArray(type, count, data, end, buff, el); nuclear@0: nuclear@0: ai_assert(data == end); nuclear@0: ai_assert(buff.size() == count * 8); nuclear@0: nuclear@0: out.reserve(count); nuclear@0: nuclear@0: const uint64_t* ip = reinterpret_cast(&buff[0]); nuclear@0: for (unsigned int i = 0; i < count; ++i, ++ip) { nuclear@0: BE_NCONST uint64_t val = *ip; nuclear@0: AI_SWAP8(val); nuclear@0: out.push_back(val); nuclear@0: } nuclear@0: nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const size_t dim = ParseTokenAsDim(*tok[0]); nuclear@0: nuclear@0: // see notes in ParseVectorDataArray() nuclear@0: out.reserve(dim); nuclear@0: nuclear@0: const Scope& scope = GetRequiredScope(el); nuclear@0: const Element& a = GetRequiredElement(scope,"a",&el); nuclear@0: nuclear@0: for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) { nuclear@0: const uint64_t ival = ParseTokenAsID(**it++); nuclear@0: nuclear@0: out.push_back(ival); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: aiMatrix4x4 ReadMatrix(const Element& element) nuclear@0: { nuclear@0: std::vector values; nuclear@0: ParseVectorDataArray(values,element); nuclear@0: nuclear@0: if(values.size() != 16) { nuclear@0: ParseError("expected 16 matrix elements"); nuclear@0: } nuclear@0: nuclear@0: aiMatrix4x4 result; nuclear@0: nuclear@0: nuclear@0: result.a1 = values[0]; nuclear@0: result.a2 = values[1]; nuclear@0: result.a3 = values[2]; nuclear@0: result.a4 = values[3]; nuclear@0: nuclear@0: result.b1 = values[4]; nuclear@0: result.b2 = values[5]; nuclear@0: result.b3 = values[6]; nuclear@0: result.b4 = values[7]; nuclear@0: nuclear@0: result.c1 = values[8]; nuclear@0: result.c2 = values[9]; nuclear@0: result.c3 = values[10]; nuclear@0: result.c4 = values[11]; nuclear@0: nuclear@0: result.d1 = values[12]; nuclear@0: result.d2 = values[13]; nuclear@0: result.d3 = values[14]; nuclear@0: result.d4 = values[15]; nuclear@0: nuclear@0: result.Transpose(); nuclear@0: return result; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // wrapper around ParseTokenAsString() with ParseError handling nuclear@0: std::string ParseTokenAsString(const Token& t) nuclear@0: { nuclear@0: const char* err; nuclear@0: const std::string& i = ParseTokenAsString(t,err); nuclear@0: if(err) { nuclear@0: ParseError(err,t); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // extract a required element from a scope, abort if the element cannot be found nuclear@0: const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/) nuclear@0: { nuclear@0: const Element* el = sc[index]; nuclear@0: if(!el) { nuclear@0: ParseError("did not find required element \"" + index + "\"",element); nuclear@0: } nuclear@0: return *el; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // extract required compound scope nuclear@0: const Scope& GetRequiredScope(const Element& el) nuclear@0: { nuclear@0: const Scope* const s = el.Compound(); nuclear@0: if(!s) { nuclear@0: ParseError("expected compound scope",&el); nuclear@0: } nuclear@0: nuclear@0: return *s; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // get token at a particular index nuclear@0: const Token& GetRequiredToken(const Element& el, unsigned int index) nuclear@0: { nuclear@0: const TokenList& t = el.Tokens(); nuclear@0: if(index >= t.size()) { nuclear@0: ParseError(Formatter::format( "missing token at index " ) << index,&el); nuclear@0: } nuclear@0: nuclear@0: return *t[index]; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // wrapper around ParseTokenAsID() with ParseError handling nuclear@0: uint64_t ParseTokenAsID(const Token& t) nuclear@0: { nuclear@0: const char* err; nuclear@0: const uint64_t i = ParseTokenAsID(t,err); nuclear@0: if(err) { nuclear@0: ParseError(err,t); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // wrapper around ParseTokenAsDim() with ParseError handling nuclear@0: size_t ParseTokenAsDim(const Token& t) nuclear@0: { nuclear@0: const char* err; nuclear@0: const size_t i = ParseTokenAsDim(t,err); nuclear@0: if(err) { nuclear@0: ParseError(err,t); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // wrapper around ParseTokenAsFloat() with ParseError handling nuclear@0: float ParseTokenAsFloat(const Token& t) nuclear@0: { nuclear@0: const char* err; nuclear@0: const float i = ParseTokenAsFloat(t,err); nuclear@0: if(err) { nuclear@0: ParseError(err,t); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // wrapper around ParseTokenAsInt() with ParseError handling nuclear@0: int ParseTokenAsInt(const Token& t) nuclear@0: { nuclear@0: const char* err; nuclear@0: const int i = ParseTokenAsInt(t,err); nuclear@0: if(err) { nuclear@0: ParseError(err,t); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: } // !FBX nuclear@0: } // !Assimp nuclear@0: nuclear@0: #endif nuclear@0: