فهرست منبع

- fbx: FINALLY got reading binary data buffers (vertices, normals etc) working! Found out fbx uses deflate to compress them.

Alexander Gessler 13 سال پیش
والد
کامیت
29b11f1cde
2فایلهای تغییر یافته به همراه395 افزوده شده و 3 حذف شده
  1. 3 3
      code/FBXBinaryTokenizer.cpp
  2. 392 0
      code/FBXParser.cpp

+ 3 - 3
code/FBXBinaryTokenizer.cpp

@@ -216,7 +216,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
 		const uint32_t length = ReadWord(input, cursor, end);
 		const uint32_t encoding = ReadWord(input, cursor, end);
 
-		const uint32_t decomp_len = ReadWord(input, cursor, end);
+		const uint32_t comp_len = ReadWord(input, cursor, end);
 
 		// compute length based on type and check against the stored value
 		if(encoding == 0) {
@@ -236,7 +236,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
 			default:
 				ai_assert(false);
 			};
-			if(length * stride != decomp_len) {
+			if(length * stride != comp_len) {
 				TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor);
 			}
 		}
@@ -244,7 +244,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
 		else if (encoding != 1) {			
 			TokenizeError("cannot ReadData, unknown encoding",input, cursor);
 		}
-		cursor += decomp_len;
+		cursor += comp_len;
 		break;
 	}
 

+ 392 - 0
code/FBXParser.cpp

@@ -45,6 +45,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#	include <zlib.h>
+#else
+#	include "../contrib/zlib/zlib.h"
+#endif
+
+
 #include "FBXTokenizer.h"
 #include "FBXParser.h"
 #include "FBXUtil.h"
@@ -461,12 +469,173 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out)
 }
 
 
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read the type code and element count of a binary data array and stop there
+void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, 
+	const Element& el)
+{
+	if (static_cast<size_t>(end-data) < 5) {
+		ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el);
+	}
+
+	// data type
+	type = *data;
+
+	// read number of elements
+	BE_NCONST uint32_t len = *reinterpret_cast<const uint32_t*>(data+1);
+	AI_SWAP4(len);
+
+	count = len;
+	data += 5;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
+void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, 
+	std::vector<char>& buff, 
+	const Element& el)
+{
+	ai_assert(static_cast<size_t>(end-data) >= 4); // runtime check for this happens at tokenization stage
+
+	BE_NCONST uint32_t encmode = *reinterpret_cast<const uint32_t*>(data);
+	AI_SWAP4(encmode);
+	data += 4;
+
+	// next comes the compressed length
+	BE_NCONST uint32_t comp_len = *reinterpret_cast<const uint32_t*>(data);
+	AI_SWAP4(comp_len);
+	data += 4;
+
+	ai_assert(data + comp_len == end);
+
+	// determine the length of the uncompressed data by looking at the type signature
+	uint32_t stride;
+	switch(type)
+	{
+	case 'f':
+	case 'i':
+		stride = 4;
+		break;
+
+	case 'd':
+	case 'l':
+		stride = 8;
+		break;
+
+	default:
+		ai_assert(false);
+	};
+
+	const uint32_t full_length = stride * count;
+	buff.resize(full_length);
+
+	if(encmode == 0) {
+		ai_assert(full_length == comp_len);
+
+		// plain data, no compression
+		std::copy(data, end, buff.begin());
+	}
+	else if(encmode == 1) {
+		// zlib/deflate, next comes ZIP head (0x78 0x01)
+		// see http://www.ietf.org/rfc/rfc1950.txt
+		
+		z_stream zstream;
+		zstream.opaque = Z_NULL;
+		zstream.zalloc = Z_NULL;
+		zstream.zfree  = Z_NULL;
+		zstream.data_type = Z_BINARY;
+
+		// http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
+		inflateInit(&zstream);
+
+		zstream.next_in   = reinterpret_cast<Bytef*>( const_cast<char*>(data) );
+		zstream.avail_in  = comp_len;
+
+		zstream.avail_out = buff.size();
+		zstream.next_out = reinterpret_cast<Bytef*>(&*buff.begin());
+		const int ret = inflate(&zstream, Z_FINISH);
+
+		if (ret != Z_STREAM_END && ret != Z_OK) {
+			ParseError("failure decompressing compressed data section");
+		}
+
+		// terminate zlib
+		inflateEnd(&zstream);
+	}
+#ifdef _DEBUG
+	else {
+		// runtime check for this happens at tokenization stage
+		ai_assert(false);
+	}
+#endif
+
+	data += comp_len;
+	ai_assert(data == end);
+}
+
+} // !anon
+
+
 // ------------------------------------------------------------------------------------------------
 // read an array of float3 tuples
 void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
 {
 	out.clear();
+
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+	
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(count % 3 != 0) {
+			ParseError("number of floats is not a multiple of three (3) (binary)",&el);
+		}
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			ParseError("expected float or double array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+		
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count3 = count / 3;
+		out.reserve(count3);
+
+		if (type != 'd') {
+			const double* d = reinterpret_cast<const double*>(&buff[0]);
+			for (unsigned int i = 0; i < count3; ++i, d += 3) {
+				out.push_back(aiVector3D(static_cast<float>(d[0]),
+					static_cast<float>(d[1]),
+					static_cast<float>(d[2])));
+			}
+		}
+		else if (type != 'f') {
+			const float* f = reinterpret_cast<const float*>(&buff[0]);
+			for (unsigned int i = 0; i < count3; ++i, f += 3) {
+				out.push_back(aiVector3D(f[0],f[1],f[2]));
+			}
+		}
+
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// may throw bad_alloc if the input is rubbish, but this need
@@ -497,6 +666,56 @@ void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(count % 4 != 0) {
+			ParseError("number of floats is not a multiple of four (4) (binary)",&el);
+		}
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			ParseError("expected float or double array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count4 = count / 4;
+		out.reserve(count4);
+
+		if (type != 'd') {
+			const double* d = reinterpret_cast<const double*>(&buff[0]);
+			for (unsigned int i = 0; i < count4; ++i, d += 4) {
+				out.push_back(aiColor4D(static_cast<float>(d[0]),
+					static_cast<float>(d[1]),
+					static_cast<float>(d[2]),
+					static_cast<float>(d[3])));
+			}
+		}
+		else if (type != 'f') {
+			const float* f = reinterpret_cast<const float*>(&buff[0]);
+			for (unsigned int i = 0; i < count4; ++i, f += 4) {
+				out.push_back(aiColor4D(f[0],f[1],f[2],f[3]));
+			}
+		}
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	//  see notes in ParseVectorDataArray() above
@@ -526,6 +745,55 @@ void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(count % 2 != 0) {
+			ParseError("number of floats is not a multiple of two (2) (binary)",&el);
+		}
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			ParseError("expected float or double array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count2 = count / 2;
+		out.reserve(count2);
+
+		if (type != 'd') {
+			const double* d = reinterpret_cast<const double*>(&buff[0]);
+			for (unsigned int i = 0; i < count2; ++i, d += 2) {
+				out.push_back(aiVector2D(static_cast<float>(d[0]),
+					static_cast<float>(d[1])));
+			}
+		}
+		else if (type != 'f') {
+			const float* f = reinterpret_cast<const float*>(&buff[0]);
+			for (unsigned int i = 0; i < count2; ++i, f += 2) {
+				out.push_back(aiVector2D(f[0],f[1]));
+			}
+		}
+
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// see notes in ParseVectorDataArray() above
@@ -553,6 +821,43 @@ void ParseVectorDataArray(std::vector<int>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'i') {
+			ParseError("expected int array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * 4);
+
+		out.reserve(count);
+
+		const uint32_t* ip = reinterpret_cast<const uint32_t*>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			BE_NCONST uint32_t val = *ip;
+			AI_SWAP4(val);
+			out.push_back(val);
+		}
+
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// see notes in ParseVectorDataArray()
@@ -574,6 +879,47 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			ParseError("expected float or double array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		if (type != 'd') {
+			const double* d = reinterpret_cast<const double*>(&buff[0]);
+			for (unsigned int i = 0; i < count; ++i, ++d) {
+				out.push_back(static_cast<float>(*d));
+			}
+		}
+		else if (type != 'f') {
+			const float* f = reinterpret_cast<const float*>(&buff[0]);
+			for (unsigned int i = 0; i < count; ++i, ++f) {
+				out.push_back(*f);
+			}
+		}
+
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// see notes in ParseVectorDataArray()
@@ -595,6 +941,15 @@ void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		// XXX don't think we need this - there is no special type sign for unsigned ints in the binary encoding
+		ParseError("feature not implemented",&el);
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// see notes in ParseVectorDataArray()
@@ -619,6 +974,43 @@ void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& el)
 {
 	out.clear();
 	const TokenList& tok = el.Tokens();
+	if(tok.empty()) {
+		ParseError("unexpected empty element",&el);
+	}
+
+	if(tok[0]->IsBinary()) {
+		const char* data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if(!count) {
+			return;
+		}
+
+		if (type != 'l') {
+			ParseError("expected long array (binary)",&el);
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		ai_assert(data == end);
+		ai_assert(buff.size() == count * 8);
+
+		out.reserve(count);
+
+		const uint64_t* ip = reinterpret_cast<const uint64_t*>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			BE_NCONST uint64_t val = *ip;
+			AI_SWAP8(val);
+			out.push_back(val);
+		}
+
+		return;
+	}
+
 	const size_t dim = ParseTokenAsDim(*tok[0]);
 
 	// see notes in ParseVectorDataArray()