diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index e3839bb95a..b90082d969 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -33,6 +33,10 @@ #include "ModelBakingLoggingCategory.h" #include "TextureBaker.h" +#ifdef HIFI_DUMP_FBX +#include "FBXToJSON.h" +#endif + void FBXBaker::bake() { qDebug() << "FBXBaker" << _modelURL << "bake starting"; @@ -187,6 +191,21 @@ void FBXBaker::importScene() { qCDebug(model_baking) << "Parsing" << _modelURL; _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); + +#ifdef HIFI_DUMP_FBX + { + FBXToJSON fbxToJSON; + fbxToJSON << _rootNode; + QFileInfo modelFile(_originalModelFilePath); + QString outFilename(_bakedOutputDir + "/" + modelFile.completeBaseName() + "_FBX.json"); + QFile jsonFile(outFilename); + if (jsonFile.open(QIODevice::WriteOnly)) { + jsonFile.write(fbxToJSON.str().c_str(), fbxToJSON.str().length()); + jsonFile.close(); + } + } +#endif + _geometry = reader.extractFBXGeometry({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -231,39 +250,40 @@ void FBXBaker::rewriteAndBakeSceneModels() { } else if (hasWarnings()) { continue; } - } - - objectChild.children.push_back(dracoMeshNode); + } else { + objectChild.children.push_back(dracoMeshNode); - static const std::vector nodeNamesToDelete { - // Node data that is packed into the draco mesh - "Vertices", - "PolygonVertexIndex", - "LayerElementNormal", - "LayerElementColor", - "LayerElementUV", - "LayerElementMaterial", - "LayerElementTexture", + static const std::vector nodeNamesToDelete { + // Node data that is packed into the draco mesh + "Vertices", + "PolygonVertexIndex", + "LayerElementNormal", + "LayerElementColor", + "LayerElementUV", + "LayerElementMaterial", + "LayerElementTexture", - // Node data that we don't support - "Edges", - "LayerElementTangent", - "LayerElementBinormal", - "LayerElementSmoothing" - }; - auto& children = objectChild.children; - auto it = children.begin(); - while (it != children.end()) { - auto begin = nodeNamesToDelete.begin(); - auto end = nodeNamesToDelete.end(); - if (find(begin, end, it->name) != end) { - it = children.erase(it); - } else { - ++it; + // Node data that we don't support + "Edges", + "LayerElementTangent", + "LayerElementBinormal", + "LayerElementSmoothing" + }; + auto& children = objectChild.children; + auto it = children.begin(); + while (it != children.end()) { + auto begin = nodeNamesToDelete.begin(); + auto end = nodeNamesToDelete.end(); + if (find(begin, end, it->name) != end) { + it = children.erase(it); + } else { + ++it; + } } } - } - } + } // Geometry Object + + } // foreach root child } } } diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 58a7bffa18..9d41209d4c 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -52,7 +52,6 @@ private: void embedTextureMetaData(); void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - void exportScene(); FBXGeometry* _geometry; QHash _textureNameMatchCount; diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index ac332f4ceb..75e10c54ab 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -24,6 +24,10 @@ #include #include +#ifdef HIFI_DUMP_FBX +#include "FBXToJSON.h" +#endif + #ifdef _WIN32 #pragma warning( pop ) #endif @@ -605,5 +609,19 @@ void ModelBaker::exportScene() { _outputFiles.push_back(_bakedModelFilePath); +#ifdef HIFI_DUMP_FBX + { + FBXToJSON fbxToJSON; + fbxToJSON << _rootNode; + QFileInfo modelFile(_bakedModelFilePath); + QString outFilename(modelFile.dir().absolutePath() + "/" + modelFile.completeBaseName() + "_FBX.json"); + QFile jsonFile(outFilename); + if (jsonFile.open(QIODevice::WriteOnly)) { + jsonFile.write(fbxToJSON.str().c_str(), fbxToJSON.str().length()); + jsonFile.close(); + } + } +#endif + qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath; } diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index ce3fc52c3a..239908f86c 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -28,8 +28,10 @@ #include #include -static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; +// See comment in FBXReader::parseFBX(). static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; +static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary "); +static const QByteArray FBX_BINARY_PROLOG2("\0\x1a\0", 3); static const quint32 FBX_VERSION_2015 = 7400; static const quint32 FBX_VERSION_2016 = 7500; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 81637e82a8..b830313723 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1603,7 +1603,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // NOTE: shapeVertices are in joint-frame std::vector shapeVertices; - shapeVertices.resize(geometry.joints.size()); + shapeVertices.resize(std::max(1, geometry.joints.size()) ); // find our special joints geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index c4454421b6..9375dc7b56 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -214,10 +214,7 @@ FBXNode parseBinaryFBXNode(QDataStream& in, int& position, bool has64BitPosition while (endOffset > position) { FBXNode child = parseBinaryFBXNode(in, position, has64BitPositions); - if (child.name.isNull()) { - return node; - - } else { + if (!child.name.isNull()) { node.children.append(child); } } diff --git a/libraries/fbx/src/FBXToJSON.cpp b/libraries/fbx/src/FBXToJSON.cpp new file mode 100644 index 0000000000..195b7f5f90 --- /dev/null +++ b/libraries/fbx/src/FBXToJSON.cpp @@ -0,0 +1,114 @@ +// +// FBXToJSON.cpp +// libraries/fbx/src +// +// Created by Simon Walton on 5/4/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FBXToJSON.h" +#include "FBX.h" + +using std::string; + +template +inline FBXToJSON& FBXToJSON::operator<<(const QVector& arrayProp) { + *this << "["; + char comma = ' '; + for (auto& prop : arrayProp) { + *(std::ostringstream*)this << comma << prop; + comma = ','; + } + *this << "] "; + + if (arrayProp.size() > 4) { + *this << "// " << arrayProp.size() << " items"; + } + *this << '\n'; + + return *this; +} + +FBXToJSON& FBXToJSON::operator<<(const FBXNode& fbxNode) { + string nodeName(fbxNode.name); + if (nodeName.empty()) { + nodeName = "node"; + } else { + nodeName = stringEscape(nodeName); + } + + *this << string(_indentLevel * 4, ' ') << '"' << nodeName << "\": {\n"; + + ++_indentLevel; + int p = 0; + const char* eol = ""; + for (auto& prop : fbxNode.properties) { + *this << eol << string(_indentLevel * 4, ' ') << "\"p" << p++ << "\": "; + switch (prop.userType()) { + case QMetaType::Short: + case QMetaType::Bool: + case QMetaType::Int: + case QMetaType::LongLong: + case QMetaType::Double: + case QMetaType::Float: + *this << prop.toString().toStdString(); + break; + + case QMetaType::QString: + case QMetaType::QByteArray: + *this << '"' << stringEscape(prop.toByteArray().toStdString()) << '"'; + break; + + default: + if (prop.canConvert>()) { + *this << prop.value>(); + } else if (prop.canConvert>()) { + *this << prop.value>(); + } else if (prop.canConvert>()) { + *this << prop.value>(); + } else if (prop.canConvert>()) { + *this << prop.value>(); + } else if (prop.canConvert>()) { + *this << prop.value>(); + } else { + *this << ""; + } + break; + } + eol = ",\n"; + } + + for (auto& child : fbxNode.children) { + *this << eol; + *this << child; + eol = ",\n"; + } + + *this << "\n" << string(_indentLevel * 4, ' ') << "}"; + --_indentLevel; + return *this; +} + +string FBXToJSON::stringEscape(const string& in) { + string out; + out.reserve(in.length()); + + for (unsigned char inChar: in) { + if (inChar == '"') { + out.append(R"(\")"); + } + else if (inChar == '\\') { + out.append(R"(\\)"); + } + else if (inChar < 0x20 || inChar == 0x7f) { + char h[5]; + sprintf(h, "\\x%02x", inChar); + out.append(h); + } + else out.append(1, inChar); + } + return out; +} diff --git a/libraries/fbx/src/FBXToJSON.h b/libraries/fbx/src/FBXToJSON.h new file mode 100644 index 0000000000..e6b8efe51d --- /dev/null +++ b/libraries/fbx/src/FBXToJSON.h @@ -0,0 +1,32 @@ +// +// FBXToJSON.h +// libraries/fbx/src +// +// Created by Simon Walton on 5/4/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FBXToJSON_h +#define hifi_FBXToJSON_h + +#include +#include + +// Forward declarations. +class FBXNode; +template class QVector; + +class FBXToJSON : public std::ostringstream { +public: + FBXToJSON& operator<<(const FBXNode& fbxNode); + +private: + template FBXToJSON& operator<<(const QVector& arrayProp); + static std::string stringEscape(const std::string& in); + int _indentLevel { 0 }; +}; + +#endif // hifi_FBXToJSON_h diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index e6adff0df9..4504898e32 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -62,8 +62,7 @@ QByteArray FBXWriter::encodeFBX(const FBXNode& root) { out.setVersion(QDataStream::Qt_4_5); out.writeRawData(FBX_BINARY_PROLOG, FBX_BINARY_PROLOG.size()); - auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0'); - out.writeRawData(bytes, bytes.size()); + out.writeRawData(FBX_BINARY_PROLOG2, FBX_BINARY_PROLOG2.size()); #ifdef USE_FBX_2016_FORMAT out << FBX_VERSION_2016;