Basic FBX parser.

This commit is contained in:
Andrzej Kapolka 2013-09-18 16:26:35 -07:00
parent 6be03ac3df
commit dfea69ab8f
3 changed files with 239 additions and 1 deletions

View file

@ -11,6 +11,7 @@
#include "Application.h"
#include "BlendFace.h"
#include "Head.h"
#include "renderer/FBXReader.h"
using namespace fs;
using namespace std;
@ -180,7 +181,12 @@ void BlendFace::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTo
_modelReply->deleteLater();
_modelReply = 0;
qDebug("Got %d bytes.\n", entirety.size());
try {
printNode(parseFBX(entirety));
} catch (const QString& error) {
qDebug() << error << "\n";
}
}
void BlendFace::handleModelReplyError() {

View file

@ -0,0 +1,192 @@
//
// FBXReader.cpp
// interface
//
// Created by Andrzej Kapolka on 9/18/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QBuffer>
#include <QDataStream>
#include <QIODevice>
#include <QtDebug>
#include <QtEndian>
#include "FBXReader.h"
FBXNode parseFBX(const QByteArray& data) {
QBuffer buffer(const_cast<QByteArray*>(&data));
buffer.open(QIODevice::ReadOnly);
return parseFBX(&buffer);
}
template<class T> QVariant readArray(QDataStream& in) {
quint32 arrayLength;
quint32 encoding;
quint32 compressedLength;
in >> arrayLength;
in >> encoding;
in >> compressedLength;
QVector<T> values;
const int DEFLATE_ENCODING = 1;
if (encoding == DEFLATE_ENCODING) {
// preface encoded data with uncompressed length
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
QByteArray uncompressed = qUncompress(compressed);
QDataStream uncompressedIn(uncompressed);
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
for (int i = 0; i < arrayLength; i++) {
T value;
uncompressedIn >> value;
values.append(value);
}
} else {
for (int i = 0; i < arrayLength; i++) {
T value;
in >> value;
values.append(value);
}
}
return QVariant::fromValue(values);
}
QVariant parseFBXProperty(QDataStream& in) {
char ch;
in.device()->getChar(&ch);
switch (ch) {
case 'Y': {
qint16 value;
in >> value;
return QVariant::fromValue(value);
}
case 'C': {
bool value;
in >> value;
return QVariant::fromValue(value);
}
case 'I': {
qint32 value;
in >> value;
return QVariant::fromValue(value);
}
case 'F': {
float value;
in >> value;
return QVariant::fromValue(value);
}
case 'D': {
double value;
in >> value;
return QVariant::fromValue(value);
}
case 'L': {
qint64 value;
in >> value;
return QVariant::fromValue(value);
}
case 'f': {
return readArray<float>(in);
}
case 'd': {
return readArray<double>(in);
}
case 'l': {
return readArray<qint64>(in);
}
case 'i': {
return readArray<qint32>(in);
}
case 'b': {
return readArray<bool>(in);
}
case 'S':
case 'R': {
quint32 length;
in >> length;
return QVariant::fromValue(in.device()->read(length));
}
default:
throw QString("Unknown property type: ") + ch;
}
}
FBXNode parseFBXNode(QDataStream& in) {
quint32 endOffset;
quint32 propertyCount;
quint32 propertyListLength;
quint8 nameLength;
in >> endOffset;
in >> propertyCount;
in >> propertyListLength;
in >> nameLength;
FBXNode node;
const int MIN_VALID_OFFSET = 40;
if (endOffset < MIN_VALID_OFFSET || nameLength == 0) {
// use a null name to indicate a null node
return node;
}
node.name = in.device()->read(nameLength);
for (int i = 0; i < propertyCount; i++) {
node.properties.append(parseFBXProperty(in));
}
while (endOffset > in.device()->pos()) {
FBXNode child = parseFBXNode(in);
if (child.name.isNull()) {
return node;
} else {
node.children.append(child);
}
}
return node;
}
FBXNode parseFBX(QIODevice* device) {
QDataStream in(device);
in.setByteOrder(QDataStream::LittleEndian);
// verify the prolog
const QByteArray EXPECTED_PROLOG = "Kaydara FBX Binary ";
if (device->read(EXPECTED_PROLOG.size()) != EXPECTED_PROLOG) {
throw QString("Invalid header.");
}
// skip the rest of the header
const int HEADER_SIZE = 27;
in.skipRawData(HEADER_SIZE - EXPECTED_PROLOG.size());
// parse the top-level node
FBXNode top;
while (device->bytesAvailable() >= 13) {
FBXNode next = parseFBXNode(in);
if (next.name.isNull()) {
return top;
} else {
top.children.append(next);
}
}
return top;
}
void printNode(const FBXNode& node, int indent) {
QByteArray spaces(indent, ' ');
qDebug("%s%s: ", spaces.data(), node.name.data());
foreach (const QVariant& property, node.properties) {
qDebug() << property;
}
qDebug() << "\n";
foreach (const FBXNode& child, node.children) {
printNode(child, indent + 1);
}
}

View file

@ -0,0 +1,40 @@
//
// FBXReader.h
// interface
//
// Created by Andrzej Kapolka on 9/18/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__FBXReader__
#define __interface__FBXReader__
#include <QVariant>
#include <QVector>
class QIODevice;
class FBXNode;
typedef QList<FBXNode> FBXNodeList;
/// A node within an FBX document.
class FBXNode {
public:
QByteArray name;
QVariantList properties;
FBXNodeList children;
};
/// Parses the input from the supplied data as an FBX file.
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(const QByteArray& data);
/// Parses the input from the supplied device as an FBX file.
/// \exception QString if an error occurs in parsing
FBXNode parseFBX(QIODevice* device);
void printNode(const FBXNode& node, int indent = 0);
#endif /* defined(__interface__FBXReader__) */