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

pull/14/head
Alexander Gessler 2012-08-11 04:16:46 +02:00
parent 1cd04a2671
commit 29b11f1cde
2 changed files with 395 additions and 3 deletions

View File

@ -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;
}

View File

@ -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()