From c57fd15bc9d480ebb8cbe8d001e2a37d1ac58331 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 10 Dec 2014 15:22:48 -0800 Subject: [PATCH] Rather than loading the entire FBX file into a byte array, read it straight from the QNetworkReply. --- interface/src/renderer/GeometryCache.cpp | 12 ++--- libraries/fbx/src/FBXReader.cpp | 56 +++++++++++++++++------- libraries/fbx/src/FBXReader.h | 6 +++ 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 2d876a287f..d753901243 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -841,14 +841,14 @@ void GeometryReader::run() { if (urlValid) { // Let's read the binaries from the network - QByteArray fileBinary = _reply->readAll(); - if (fileBinary.isEmpty() || fileBinary.isNull()) { - throw QString("Read File binary is empty?!"); - } - FBXGeometry fbxgeo; if (_url.path().toLower().endsWith(".svo")) { + QByteArray fileBinary = _reply->readAll(); + if (fileBinary.isEmpty() || fileBinary.isNull()) { + throw QString("Read File binary is empty?!"); + } fbxgeo = readSVO(fileBinary); + } else if (_url.path().toLower().endsWith(".fbx")) { bool grabLightmaps = true; float lightmapLevel = 1.0f; @@ -860,7 +860,7 @@ void GeometryReader::run() { } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { lightmapLevel = 3.5f; } - fbxgeo = readFBX(fileBinary, _mapping, grabLightmaps, lightmapLevel); + fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel); } QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); } else { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d52d342d78..84c9d1800f 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -138,7 +138,15 @@ static int fbxGeometryMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); -template QVariant readBinaryArray(QDataStream& in) { +template int streamSize() { + return sizeof(T); +} + +template int streamSize() { + return 1; +} + +template QVariant readBinaryArray(QDataStream& in, int& position) { quint32 arrayLength; quint32 encoding; quint32 compressedLength; @@ -146,6 +154,7 @@ template QVariant readBinaryArray(QDataStream& in) { in >> arrayLength; in >> encoding; in >> compressedLength; + position += sizeof(quint32) * 3; QVector values; const unsigned int DEFLATE_ENCODING = 1; @@ -154,6 +163,7 @@ template QVariant readBinaryArray(QDataStream& in) { QByteArray compressed(sizeof(quint32) + compressedLength, 0); *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); in.readRawData(compressed.data() + sizeof(quint32), compressedLength); + position += compressedLength; QByteArray uncompressed = qUncompress(compressed); QDataStream uncompressedIn(uncompressed); uncompressedIn.setByteOrder(QDataStream::LittleEndian); @@ -167,65 +177,74 @@ template QVariant readBinaryArray(QDataStream& in) { for (quint32 i = 0; i < arrayLength; i++) { T value; in >> value; + position += streamSize(); values.append(value); } } return QVariant::fromValue(values); } -QVariant parseBinaryFBXProperty(QDataStream& in) { +QVariant parseBinaryFBXProperty(QDataStream& in, int& position) { char ch; in.device()->getChar(&ch); + position++; switch (ch) { case 'Y': { qint16 value; in >> value; + position += sizeof(qint16); return QVariant::fromValue(value); } case 'C': { bool value; in >> value; + position++; return QVariant::fromValue(value); } case 'I': { qint32 value; in >> value; + position += sizeof(qint32); return QVariant::fromValue(value); } case 'F': { float value; in >> value; + position += sizeof(float); return QVariant::fromValue(value); } case 'D': { double value; in >> value; + position += sizeof(double); return QVariant::fromValue(value); } case 'L': { qint64 value; in >> value; + position += sizeof(qint64); return QVariant::fromValue(value); } case 'f': { - return readBinaryArray(in); + return readBinaryArray(in, position); } case 'd': { - return readBinaryArray(in); + return readBinaryArray(in, position); } case 'l': { - return readBinaryArray(in); + return readBinaryArray(in, position); } case 'i': { - return readBinaryArray(in); + return readBinaryArray(in, position); } case 'b': { - return readBinaryArray(in); + return readBinaryArray(in, position); } case 'S': case 'R': { quint32 length; in >> length; + position += sizeof(quint32) + length; return QVariant::fromValue(in.device()->read(length)); } default: @@ -233,8 +252,8 @@ QVariant parseBinaryFBXProperty(QDataStream& in) { } } -FBXNode parseBinaryFBXNode(QDataStream& in) { - quint32 endOffset; +FBXNode parseBinaryFBXNode(QDataStream& in, int& position) { + qint32 endOffset; quint32 propertyCount; quint32 propertyListLength; quint8 nameLength; @@ -243,21 +262,23 @@ FBXNode parseBinaryFBXNode(QDataStream& in) { in >> propertyCount; in >> propertyListLength; in >> nameLength; + position += sizeof(quint32) * 3 + sizeof(quint8); FBXNode node; - const unsigned int MIN_VALID_OFFSET = 40; + 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); + position += nameLength; for (quint32 i = 0; i < propertyCount; i++) { - node.properties.append(parseBinaryFBXProperty(in)); + node.properties.append(parseBinaryFBXProperty(in, position)); } - while (endOffset > in.device()->pos()) { - FBXNode child = parseBinaryFBXNode(in); + while (endOffset > position) { + FBXNode child = parseBinaryFBXNode(in, position); if (child.name.isNull()) { return node; @@ -416,11 +437,12 @@ FBXNode parseFBX(QIODevice* device) { // skip the rest of the header const int HEADER_SIZE = 27; in.skipRawData(HEADER_SIZE); + int position = HEADER_SIZE; // parse the top-level node FBXNode top; while (device->bytesAvailable()) { - FBXNode next = parseBinaryFBXNode(in); + FBXNode next = parseBinaryFBXNode(in, position); if (next.name.isNull()) { return top; @@ -2516,7 +2538,11 @@ QByteArray writeMapping(const QVariantHash& mapping) { FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); - return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps, lightmapLevel); + return readFBX(&buffer, mapping, loadLightmaps, lightmapLevel); +} + +FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { + return extractFBXGeometry(parseFBX(device), mapping, loadLightmaps, lightmapLevel); } bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 659893c128..736da3a349 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -24,6 +24,8 @@ #include #include +class QIODevice; + class FBXNode; typedef QList FBXNodeList; @@ -272,6 +274,10 @@ QByteArray writeMapping(const QVariantHash& mapping); /// \exception QString if an error occurs in parsing FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f); +/// Reads FBX geometry from the supplied model and mapping data. +/// \exception QString if an error occurs in parsing +FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f); + /// Reads SVO geometry from the supplied model data. FBXGeometry readSVO(const QByteArray& model);