From aa1aad0a0995fbe7db54209aa7a347e5bf228033 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 10:36:19 -0700 Subject: [PATCH 01/40] Add FBXWriter for serializing FBXNode --- libraries/fbx/src/FBX.cpp | 10 + libraries/fbx/src/FBX.h | 333 +++++++++++++++++++++++++++ libraries/fbx/src/FBXReader.cpp | 11 +- libraries/fbx/src/FBXReader.h | 303 +----------------------- libraries/fbx/src/FBXReader_Node.cpp | 20 +- libraries/fbx/src/FBXWriter.cpp | 253 ++++++++++++++++++++ libraries/fbx/src/FBXWriter.h | 28 +++ 7 files changed, 641 insertions(+), 317 deletions(-) create mode 100644 libraries/fbx/src/FBX.cpp create mode 100644 libraries/fbx/src/FBX.h create mode 100644 libraries/fbx/src/FBXWriter.cpp create mode 100644 libraries/fbx/src/FBXWriter.h diff --git a/libraries/fbx/src/FBX.cpp b/libraries/fbx/src/FBX.cpp new file mode 100644 index 0000000000..3f18b3e678 --- /dev/null +++ b/libraries/fbx/src/FBX.cpp @@ -0,0 +1,10 @@ +// +// FBX.cpp +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/17. +// Copyright 2017 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 +// diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h new file mode 100644 index 0000000000..9e1982f9e0 --- /dev/null +++ b/libraries/fbx/src/FBX.h @@ -0,0 +1,333 @@ +// +// FBX.h +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/17. +// Copyright 2017 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_FBX_h_ +#define hifi_FBX_h_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; +static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; +static const quint32 FBX_VERSION_2016 = 7500; + +class FBXNode; +using FBXNodeList = QList; + + +/// A node within an FBX document. +class FBXNode { +public: + QByteArray name; + QVariantList properties; + FBXNodeList children; +}; + + +/// A single blendshape extracted from an FBX document. +class FBXBlendshape { +public: + QVector indices; + QVector vertices; + QVector normals; +}; + +struct FBXJointShapeInfo { + // same units and frame as FBXJoint.translation + glm::vec3 avgPoint; + std::vector dots; + std::vector points; + std::vector debugLines; +}; + +/// A single joint (transformation node) extracted from an FBX document. +class FBXJoint { +public: + + FBXJointShapeInfo shapeInfo; + QVector freeLineage; + bool isFree; + int parentIndex; + float distanceToParent; + + // http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html + + glm::vec3 translation; // T + glm::mat4 preTransform; // Roff * Rp + glm::quat preRotation; // Rpre + glm::quat rotation; // R + glm::quat postRotation; // Rpost + glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1 + + // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) + + glm::mat4 transform; + glm::vec3 rotationMin; // radians + glm::vec3 rotationMax; // radians + glm::quat inverseDefaultRotation; + glm::quat inverseBindRotation; + glm::mat4 bindTransform; + QString name; + bool isSkeletonJoint; + bool bindTransformFoundInCluster; + + // geometric offset is applied in local space but does NOT affect children. + bool hasGeometricOffset; + glm::vec3 geometricTranslation; + glm::quat geometricRotation; + glm::vec3 geometricScaling; +}; + + +/// A single binding to a joint in an FBX document. +class FBXCluster { +public: + + int jointIndex; + glm::mat4 inverseBindMatrix; +}; + +const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; + +/// A texture map in an FBX document. +class FBXTexture { +public: + QString name; + QByteArray filename; + QByteArray content; + + Transform transform; + int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE }; + int texcoordSet; + QString texcoordSetName; + + bool isBumpmap{ false }; + + bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } +}; + +/// A single part of a mesh (with the same material). +class FBXMeshPart { +public: + + QVector quadIndices; // original indices from the FBX mesh + QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles + QVector triangleIndices; // original indices from the FBX mesh + + QString materialID; +}; + +class FBXMaterial { +public: + FBXMaterial() {}; + FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + float shininess, float opacity) : + diffuseColor(diffuseColor), + specularColor(specularColor), + emissiveColor(emissiveColor), + shininess(shininess), + opacity(opacity) {} + + void getTextureNames(QSet& textureList) const; + void setMaxNumPixelsPerTexture(int maxNumPixels); + + glm::vec3 diffuseColor{ 1.0f }; + float diffuseFactor{ 1.0f }; + glm::vec3 specularColor{ 0.02f }; + float specularFactor{ 1.0f }; + + glm::vec3 emissiveColor{ 0.0f }; + float emissiveFactor{ 0.0f }; + + float shininess{ 23.0f }; + float opacity{ 1.0f }; + + float metallic{ 0.0f }; + float roughness{ 1.0f }; + float emissiveIntensity{ 1.0f }; + float ambientFactor{ 1.0f }; + + QString materialID; + QString name; + QString shadingModel; + model::MaterialPointer _material; + + FBXTexture normalTexture; + FBXTexture albedoTexture; + FBXTexture opacityTexture; + FBXTexture glossTexture; + FBXTexture roughnessTexture; + FBXTexture specularTexture; + FBXTexture metallicTexture; + FBXTexture emissiveTexture; + FBXTexture occlusionTexture; + FBXTexture scatteringTexture; + FBXTexture lightmapTexture; + glm::vec2 lightmapParams{ 0.0f, 1.0f }; + + + bool isPBSMaterial{ false }; + // THe use XXXMap are not really used to drive which map are going or not, debug only + bool useNormalMap{ false }; + bool useAlbedoMap{ false }; + bool useOpacityMap{ false }; + bool useRoughnessMap{ false }; + bool useSpecularMap{ false }; + bool useMetallicMap{ false }; + bool useEmissiveMap{ false }; + bool useOcclusionMap{ false }; + + bool needTangentSpace() const; +}; + +/// A single mesh (with optional blendshapes) extracted from an FBX document. +class FBXMesh { +public: + + QVector parts; + + QVector vertices; + QVector normals; + QVector tangents; + QVector colors; + QVector texCoords; + QVector texCoords1; + QVector clusterIndices; + QVector clusterWeights; + + QVector clusters; + + Extents meshExtents; + glm::mat4 modelTransform; + + QVector blendshapes; + + unsigned int meshIndex; // the order the meshes appeared in the object file + + model::MeshPointer _mesh; +}; + +class ExtractedMesh { +public: + FBXMesh mesh; + QMultiHash newIndices; + QVector > blendshapeIndexMaps; + QVector > partMaterialTextures; + QHash texcoordSetMap; +}; + +/// A single animation frame extracted from an FBX document. +class FBXAnimationFrame { +public: + QVector rotations; + QVector translations; +}; + +/// A light in an FBX document. +class FBXLight { +public: + QString name; + Transform transform; + float intensity; + float fogValue; + glm::vec3 color; + + FBXLight() : + name(), + transform(), + intensity(1.0f), + fogValue(0.0f), + color(1.0f) + {} +}; + +Q_DECLARE_METATYPE(FBXAnimationFrame) +Q_DECLARE_METATYPE(QVector) + +/// A set of meshes extracted from an FBX document. +class FBXGeometry { +public: + using Pointer = std::shared_ptr; + + QString originalURL; + QString author; + QString applicationName; ///< the name of the application that generated the model + + QVector joints; + QHash jointIndices; ///< 1-based, so as to more easily detect missing indices + bool hasSkeletonJoints; + + QVector meshes; + + QHash materials; + + glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file + + int leftEyeJointIndex = -1; + int rightEyeJointIndex = -1; + int neckJointIndex = -1; + int rootJointIndex = -1; + int leanJointIndex = -1; + int headJointIndex = -1; + int leftHandJointIndex = -1; + int rightHandJointIndex = -1; + int leftToeJointIndex = -1; + int rightToeJointIndex = -1; + + float leftEyeSize = 0.0f; // Maximum mesh extents dimension + float rightEyeSize = 0.0f; + + QVector humanIKJointIndices; + + glm::vec3 palmDirection; + + glm::vec3 neckPivot; + + Extents bindExtents; + Extents meshExtents; + + QVector animationFrames; + + int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } + QStringList getJointNames() const; + + bool hasBlendedMeshes() const; + + /// Returns the unscaled extents of the model's mesh + Extents getUnscaledMeshExtents() const; + + bool convexHullContains(const glm::vec3& point) const; + + QHash meshIndicesToModelNames; + + /// given a meshIndex this will return the name of the model that mesh belongs to if known + QString getModelNameOfMesh(int meshIndex) const; + + QList blendshapeChannelNames; +}; + +Q_DECLARE_METATYPE(FBXGeometry) +Q_DECLARE_METATYPE(FBXGeometry::Pointer) + +#endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 450aa296d8..ea4b00523e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -168,7 +168,8 @@ QString getID(const QVariantList& properties, int index = 0) { return processID(properties.at(index).toString()); } -const char* HUMANIK_JOINTS[] = { +/// The names of the joints in the Maya HumanIK rig +static const std::array HUMANIK_JOINTS = { "RightHand", "RightForeArm", "RightArm", @@ -184,8 +185,7 @@ const char* HUMANIK_JOINTS[] = { "RightLeg", "LeftLeg", "RightFoot", - "LeftFoot", - "" + "LeftFoot" }; class FBXModel { @@ -512,11 +512,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector humanIKJointNames; - for (int i = 0;; i++) { + for (int i = 0; i < HUMANIK_JOINTS.size(); i++) { QByteArray jointName = HUMANIK_JOINTS[i]; - if (jointName.isEmpty()) { - break; - } humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); } QVector humanIKJointIDs(humanIKJointNames.size()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 170bbbf366..a600ac6bab 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -12,6 +12,8 @@ #ifndef hifi_FBXReader_h #define hifi_FBXReader_h +#include "FBX.h" + #include #include #include @@ -31,305 +33,6 @@ class QIODevice; class FBXNode; -typedef QList FBXNodeList; - -/// The names of the joints in the Maya HumanIK rig, terminated with an empty string. -extern const char* HUMANIK_JOINTS[]; - -/// A node within an FBX document. -class FBXNode { -public: - - QByteArray name; - QVariantList properties; - FBXNodeList children; -}; - -/// A single blendshape extracted from an FBX document. -class FBXBlendshape { -public: - - QVector indices; - QVector vertices; - QVector normals; -}; - -struct FBXJointShapeInfo { - // same units and frame as FBXJoint.translation - glm::vec3 avgPoint; - std::vector dots; - std::vector points; - std::vector debugLines; -}; - -/// A single joint (transformation node) extracted from an FBX document. -class FBXJoint { -public: - - FBXJointShapeInfo shapeInfo; - QVector freeLineage; - bool isFree; - int parentIndex; - float distanceToParent; - - // http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html - - glm::vec3 translation; // T - glm::mat4 preTransform; // Roff * Rp - glm::quat preRotation; // Rpre - glm::quat rotation; // R - glm::quat postRotation; // Rpost - glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1 - - // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) - - glm::mat4 transform; - glm::vec3 rotationMin; // radians - glm::vec3 rotationMax; // radians - glm::quat inverseDefaultRotation; - glm::quat inverseBindRotation; - glm::mat4 bindTransform; - QString name; - bool isSkeletonJoint; - bool bindTransformFoundInCluster; - - // geometric offset is applied in local space but does NOT affect children. - bool hasGeometricOffset; - glm::vec3 geometricTranslation; - glm::quat geometricRotation; - glm::vec3 geometricScaling; -}; - - -/// A single binding to a joint in an FBX document. -class FBXCluster { -public: - - int jointIndex; - glm::mat4 inverseBindMatrix; -}; - -const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; - -/// A texture map in an FBX document. -class FBXTexture { -public: - QString name; - QByteArray filename; - QByteArray content; - - Transform transform; - int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE }; - int texcoordSet; - QString texcoordSetName; - - bool isBumpmap{ false }; - - bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } -}; - -/// A single part of a mesh (with the same material). -class FBXMeshPart { -public: - - QVector quadIndices; // original indices from the FBX mesh - QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles - QVector triangleIndices; // original indices from the FBX mesh - - QString materialID; -}; - -class FBXMaterial { -public: - FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, - float shininess, float opacity) : - diffuseColor(diffuseColor), - specularColor(specularColor), - emissiveColor(emissiveColor), - shininess(shininess), - opacity(opacity) {} - - void getTextureNames(QSet& textureList) const; - void setMaxNumPixelsPerTexture(int maxNumPixels); - - glm::vec3 diffuseColor{ 1.0f }; - float diffuseFactor{ 1.0f }; - glm::vec3 specularColor{ 0.02f }; - float specularFactor{ 1.0f }; - - glm::vec3 emissiveColor{ 0.0f }; - float emissiveFactor{ 0.0f }; - - float shininess{ 23.0f }; - float opacity{ 1.0f }; - - float metallic{ 0.0f }; - float roughness{ 1.0f }; - float emissiveIntensity{ 1.0f }; - float ambientFactor{ 1.0f }; - - QString materialID; - QString name; - QString shadingModel; - model::MaterialPointer _material; - - FBXTexture normalTexture; - FBXTexture albedoTexture; - FBXTexture opacityTexture; - FBXTexture glossTexture; - FBXTexture roughnessTexture; - FBXTexture specularTexture; - FBXTexture metallicTexture; - FBXTexture emissiveTexture; - FBXTexture occlusionTexture; - FBXTexture scatteringTexture; - FBXTexture lightmapTexture; - glm::vec2 lightmapParams{ 0.0f, 1.0f }; - - - bool isPBSMaterial{ false }; - // THe use XXXMap are not really used to drive which map are going or not, debug only - bool useNormalMap{ false }; - bool useAlbedoMap{ false }; - bool useOpacityMap{ false }; - bool useRoughnessMap{ false }; - bool useSpecularMap{ false }; - bool useMetallicMap{ false }; - bool useEmissiveMap{ false }; - bool useOcclusionMap{ false }; - - bool needTangentSpace() const; -}; - -/// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { -public: - - QVector parts; - - QVector vertices; - QVector normals; - QVector tangents; - QVector colors; - QVector texCoords; - QVector texCoords1; - QVector clusterIndices; - QVector clusterWeights; - - QVector clusters; - - Extents meshExtents; - glm::mat4 modelTransform; - - QVector blendshapes; - - unsigned int meshIndex; // the order the meshes appeared in the object file - - model::MeshPointer _mesh; -}; - -class ExtractedMesh { -public: - FBXMesh mesh; - QMultiHash newIndices; - QVector > blendshapeIndexMaps; - QVector > partMaterialTextures; - QHash texcoordSetMap; -}; - -/// A single animation frame extracted from an FBX document. -class FBXAnimationFrame { -public: - QVector rotations; - QVector translations; -}; - -/// A light in an FBX document. -class FBXLight { -public: - QString name; - Transform transform; - float intensity; - float fogValue; - glm::vec3 color; - - FBXLight() : - name(), - transform(), - intensity(1.0f), - fogValue(0.0f), - color(1.0f) - {} -}; - -Q_DECLARE_METATYPE(FBXAnimationFrame) -Q_DECLARE_METATYPE(QVector) - -/// A set of meshes extracted from an FBX document. -class FBXGeometry { -public: - using Pointer = std::shared_ptr; - - QString originalURL; - QString author; - QString applicationName; ///< the name of the application that generated the model - - QVector joints; - QHash jointIndices; ///< 1-based, so as to more easily detect missing indices - bool hasSkeletonJoints; - - QVector meshes; - - QHash materials; - - glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file - - int leftEyeJointIndex = -1; - int rightEyeJointIndex = -1; - int neckJointIndex = -1; - int rootJointIndex = -1; - int leanJointIndex = -1; - int headJointIndex = -1; - int leftHandJointIndex = -1; - int rightHandJointIndex = -1; - int leftToeJointIndex = -1; - int rightToeJointIndex = -1; - - float leftEyeSize = 0.0f; // Maximum mesh extents dimension - float rightEyeSize = 0.0f; - - QVector humanIKJointIndices; - - glm::vec3 palmDirection; - - glm::vec3 neckPivot; - - Extents bindExtents; - Extents meshExtents; - - QVector animationFrames; - - int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } - QStringList getJointNames() const; - - bool hasBlendedMeshes() const; - - /// Returns the unscaled extents of the model's mesh - Extents getUnscaledMeshExtents() const; - - bool convexHullContains(const glm::vec3& point) const; - - QHash meshIndicesToModelNames; - - /// given a meshIndex this will return the name of the model that mesh belongs to if known - QString getModelNameOfMesh(int meshIndex) const; - - QList blendshapeChannelNames; -}; - -Q_DECLARE_METATYPE(FBXGeometry) -Q_DECLARE_METATYPE(FBXGeometry::Pointer) /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing @@ -402,7 +105,7 @@ class FBXReader { public: FBXGeometry* _fbxGeometry; - FBXNode _fbxNode; + FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index d987f885eb..111b4a295a 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -24,15 +24,18 @@ #include #include "ModelFormatLogging.h" -template int streamSize() { +template +int streamSize() { return sizeof(T); } -template int streamSize() { +template +int streamSize() { return 1; } -template QVariant readBinaryArray(QDataStream& in, int& position) { +template +QVariant readBinaryArray(QDataStream& in, int& position) { quint32 arrayLength; quint32 encoding; quint32 compressedLength; @@ -350,8 +353,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) { FBXNode FBXReader::parseFBX(QIODevice* device) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xff0000ff, device); // verify the prolog - const QByteArray BINARY_PROLOG = "Kaydara FBX Binary "; - if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) { + if (device->peek(FBX_BINARY_PROLOG.size()) != FBX_BINARY_PROLOG) { // parse as a text file FBXNode top; Tokenizer tokenizer(device); @@ -377,15 +379,13 @@ FBXNode FBXReader::parseFBX(QIODevice* device) { // Bytes 0 - 20: Kaydara FBX Binary \x00(file - magic, with 2 spaces at the end, then a NULL terminator). // Bytes 21 - 22: [0x1A, 0x00](unknown but all observed files show these bytes). // Bytes 23 - 26 : unsigned int, the version number. 7300 for version 7.3 for example. - const int HEADER_BEFORE_VERSION = 23; - const quint32 VERSION_FBX2016 = 7500; - in.skipRawData(HEADER_BEFORE_VERSION); - int position = HEADER_BEFORE_VERSION; + in.skipRawData(FBX_HEADER_BYTES_BEFORE_VERSION); + int position = FBX_HEADER_BYTES_BEFORE_VERSION; quint32 fileVersion; in >> fileVersion; position += sizeof(fileVersion); qCDebug(modelformat) << "fileVersion:" << fileVersion; - bool has64BitPositions = (fileVersion >= VERSION_FBX2016); + bool has64BitPositions = (fileVersion >= FBX_VERSION_2016); // parse the top-level node FBXNode top; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp new file mode 100644 index 0000000000..a084d94a46 --- /dev/null +++ b/libraries/fbx/src/FBXWriter.cpp @@ -0,0 +1,253 @@ +// +// FBXWriter.cpp +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/17. +// Copyright 2017 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 "FBXWriter.h" + +#include + + +QByteArray FBXWriter::encodeFBX(const FBXNode& root) { + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out.setByteOrder(QDataStream::LittleEndian); + 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 << FBX_VERSION_2016; + + for (auto& child : root.children) { + encodeNode(out, child); + } + encodeNode(out, FBXNode()); + + return data; +} + +void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { + qDebug() << "Encoding " << node.name; + + auto device = out.device(); + auto nodeStartPos = device->pos(); + + // endOffset (temporary, updated later) + out << (qint64)0; + + // Property count + out << (quint64)node.properties.size(); + + // Property list length (temporary, updated later) + out << (quint64)0; + + out << (quint8)node.name.size(); + out.writeRawData(node.name, node.name.size()); + + if (node.name == "Vertices") { + for (auto& prop : node.properties) { + qDebug() << "Properties: " << prop; + } + } + + auto nodePropertiesStartPos = device->pos(); + + for (const auto& prop : node.properties) { + encodeFBXProperty(out, prop); + } + + // Go back and write property list length + auto nodePropertiesEndPos = device->pos(); + device->seek(nodeStartPos + sizeof(qint64) + sizeof(quint64)); + out << (quint64)(nodePropertiesEndPos - nodePropertiesStartPos); + + device->seek(nodePropertiesEndPos); + + for (auto& child : node.children) { + encodeNode(out, child); + } + + if (node.children.length() > 0) { + encodeNode(out, FBXNode()); + } + + // Go back and write actual endOffset + auto nodeEndPos = device->pos(); + device->seek(nodeStartPos); + out << (qint64)(nodeEndPos); + + device->seek(nodeEndPos); +} + +void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { + auto type = prop.userType(); + switch (type) { + case QVariant::Type::Bool: + + out.device()->write("C", 1); + out << prop.toBool(); + break; + + case QMetaType::Int: + out.device()->write("I", 1); + out << prop.toInt(); + break; + + encodeNode(out, FBXNode()); + case QMetaType::Float: + out.device()->write("F", 1); + out << prop.toFloat(); + break; + + case QMetaType::Double: + out.device()->write("D", 1); + out << prop.toDouble(); + break; + + case QMetaType::LongLong: + out.device()->write("L", 1); + out << prop.toLongLong(); + break; + + case QMetaType::QString: + { + auto& bytes = prop.toString().toUtf8(); + out << 'S'; + out << bytes.length(); + out << bytes; + out << (int32_t)bytes.size(); + out.writeRawData(bytes, bytes.size()); + break; + } + + case QMetaType::QByteArray: + { + auto& bytes = prop.toByteArray(); + out.device()->write("S", 1); + out << (int32_t)bytes.size(); + out.writeRawData(bytes, bytes.size()); + break; + } + + // TODO Delete? Do we ever use QList instead of QVector? + case QVariant::Type::List: + { + auto& list = prop.toList(); + auto listType = prop.userType(); + + switch (listType) { + case QMetaType::Float: + out.device()->write("f", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toFloat(); + } + break; + + case QMetaType::Double: + out.device()->write("d", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toDouble(); + } + break; + + case QMetaType::LongLong: + out.device()->write("l", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toLongLong(); + } + break; + + case QMetaType::Int: + out.device()->write("i", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toInt(); + } + break; + + case QMetaType::Bool: + out.device()->write("b", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toBool(); + } + break; + } + } + break; + + default: + { + if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("f", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("d", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("l", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("i", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("b", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else { + qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop; + } + } + + } +} diff --git a/libraries/fbx/src/FBXWriter.h b/libraries/fbx/src/FBXWriter.h new file mode 100644 index 0000000000..fa33983345 --- /dev/null +++ b/libraries/fbx/src/FBXWriter.h @@ -0,0 +1,28 @@ +// +// FBXWriter.h +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/17. +// Copyright 2017 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_FBXWriter_h +#define hifi_FBXWriter_h + +#include "FBX.h" + +#include +#include + +class FBXWriter { +public: + static QByteArray encodeFBX(const FBXNode& root); + + static void encodeNode(QDataStream& out, const FBXNode& node); + static void encodeFBXProperty(QDataStream& out, const QVariant& property); +}; + +#endif // hifi_FBXWriter_h From 7a55c867be3ee6b7a3c55455ff1533abf8daed3e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 11:49:37 -0700 Subject: [PATCH 02/40] Fix _fbxNode rename to _rootNode in FBXReader --- libraries/fbx/src/FBXReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ea4b00523e..d212ec820f 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -468,7 +468,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { } FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { - const FBXNode& node = _fbxNode; + const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; @@ -1839,7 +1839,7 @@ FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; - reader._fbxNode = FBXReader::parseFBX(device); + reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; reader._lightmapLevel = lightmapLevel; From 49e09f483872c5ccca2d1137470a87099a29abfc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 13:12:07 -0700 Subject: [PATCH 03/40] Remove FBXBaker and TextureBaker from oven --- tools/oven/src/FBXBaker.cpp | 568 -------------------------------- tools/oven/src/FBXBaker.h | 103 ------ tools/oven/src/TextureBaker.cpp | 131 -------- tools/oven/src/TextureBaker.h | 59 ---- 4 files changed, 861 deletions(-) delete mode 100644 tools/oven/src/FBXBaker.cpp delete mode 100644 tools/oven/src/FBXBaker.h delete mode 100644 tools/oven/src/TextureBaker.cpp delete mode 100644 tools/oven/src/TextureBaker.h diff --git a/tools/oven/src/FBXBaker.cpp b/tools/oven/src/FBXBaker.cpp deleted file mode 100644 index 8ece76b6c4..0000000000 --- a/tools/oven/src/FBXBaker.cpp +++ /dev/null @@ -1,568 +0,0 @@ -// -// FBXBaker.cpp -// tools/oven/src -// -// Created by Stephen Birarda on 3/30/17. -// Copyright 2017 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 // need this include so we don't get an error looking for std::isnan - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include "ModelBakingLoggingCategory.h" -#include "TextureBaker.h" - -#include "FBXBaker.h" - -std::once_flag onceFlag; -FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; - -FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir) : - _fbxURL(fbxURL), - _bakedOutputDir(bakedOutputDir), - _originalOutputDir(originalOutputDir), - _textureThreadGetter(textureThreadGetter) -{ - std::call_once(onceFlag, [](){ - // create the static FBX SDK manager - _sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){ - manager->Destroy(); - }); - }); -} - -void FBXBaker::bake() { - auto tempDir = PathUtils::generateTemporaryDir(); - - if (tempDir.isEmpty()) { - handleError("Failed to create a temporary directory."); - return; - } - - _tempDir = tempDir; - - _originalFBXFilePath = _tempDir.filePath(_fbxURL.fileName()); - qDebug() << "Made temporary dir " << _tempDir; - qDebug() << "Origin file path: " << _originalFBXFilePath; - - // setup the output folder for the results of this bake - setupOutputFolder(); - - if (hasErrors()) { - return; - } - - connect(this, &FBXBaker::sourceCopyReadyToLoad, this, &FBXBaker::bakeSourceCopy); - - // make a local copy of the FBX file - loadSourceFBX(); -} - -void FBXBaker::bakeSourceCopy() { - // load the scene from the FBX file - importScene(); - - if (hasErrors()) { - return; - } - - // enumerate the textures found in the scene and start a bake for them - rewriteAndBakeSceneTextures(); - - if (hasErrors()) { - return; - } - - // export the FBX with re-written texture references - exportScene(); - - if (hasErrors()) { - return; - } - - // check if we're already done with textures (in case we had none to re-write) - checkIfTexturesFinished(); -} - -void FBXBaker::setupOutputFolder() { - // make sure there isn't already an output directory using the same name - int iteration = 0; - - if (QDir(_bakedOutputDir).exists()) { - qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; - //_bakedOutputDir = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; - } else { - qCDebug(model_baking) << "Creating FBX output folder" << _bakedOutputDir; - - // attempt to make the output folder - if (!QDir().mkpath(_bakedOutputDir)) { - handleError("Failed to create FBX output folder " + _bakedOutputDir); - return; - } - // attempt to make the output folder - if (!QDir().mkpath(_originalOutputDir)) { - handleError("Failed to create FBX output folder " + _bakedOutputDir); - return; - } - } -} - -void FBXBaker::loadSourceFBX() { - // check if the FBX is local or first needs to be downloaded - if (_fbxURL.isLocalFile()) { - // load up the local file - QFile localFBX { _fbxURL.toLocalFile() }; - - qDebug() << "Local file url: " << _fbxURL << _fbxURL.toString() << _fbxURL.toLocalFile() << ", copying to: " << _originalFBXFilePath; - - if (!localFBX.exists()) { - //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); - handleError("Could not find " + _fbxURL.toString()); - return; - } - - // make a copy in the output folder - if (!_originalOutputDir.isEmpty()) { - qDebug() << "Copying to: " << _originalOutputDir << "/" << _fbxURL.fileName(); - localFBX.copy(_originalOutputDir + "/" + _fbxURL.fileName()); - } - - localFBX.copy(_originalFBXFilePath); - - // emit our signal to start the import of the FBX source copy - emit sourceCopyReadyToLoad(); - } else { - // remote file, kick off a download - auto& networkAccessManager = NetworkAccessManager::getInstance(); - - QNetworkRequest networkRequest; - - // setup the request to follow re-directs and always hit the network - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - - - networkRequest.setUrl(_fbxURL); - - qCDebug(model_baking) << "Downloading" << _fbxURL; - auto networkReply = networkAccessManager.get(networkRequest); - - connect(networkReply, &QNetworkReply::finished, this, &FBXBaker::handleFBXNetworkReply); - } -} - -void FBXBaker::handleFBXNetworkReply() { - auto requestReply = qobject_cast(sender()); - - if (requestReply->error() == QNetworkReply::NoError) { - qCDebug(model_baking) << "Downloaded" << _fbxURL; - - // grab the contents of the reply and make a copy in the output folder - QFile copyOfOriginal(_originalFBXFilePath); - - qDebug(model_baking) << "Writing copy of original FBX to" << _originalFBXFilePath << copyOfOriginal.fileName(); - - if (!copyOfOriginal.open(QIODevice::WriteOnly)) { - // add an error to the error list for this FBX stating that a duplicate of the original FBX could not be made - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to open " + _originalFBXFilePath + ")"); - return; - } - if (copyOfOriginal.write(requestReply->readAll()) == -1) { - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to write)"); - return; - } - - // close that file now that we are done writing to it - copyOfOriginal.close(); - - if (!_originalOutputDir.isEmpty()) { - copyOfOriginal.copy(_originalOutputDir + "/" + _fbxURL.fileName()); - } - - // emit our signal to start the import of the FBX source copy - emit sourceCopyReadyToLoad(); - } else { - // add an error to our list stating that the FBX could not be downloaded - handleError("Failed to download " + _fbxURL.toString()); - } -} - -void FBXBaker::importScene() { - // create an FBX SDK importer - FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); - - qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); - // import the copy of the original FBX file - bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); - - if (!importStatus) { - // failed to initialize importer, print an error and return - handleError("Failed to import " + _fbxURL.toString() + " - " + importer->GetStatus().GetErrorString()); - return; - } else { - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; - } - - // setup a new scene to hold the imported file - _scene = FbxScene::Create(_sdkManager.get(), "bakeScene"); - - // import the file to the created scene - importer->Import(_scene); - - // destroy the importer that is no longer needed - importer->Destroy(); -} - -QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { - auto fbxPath = fbxURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - - if (texturePath.startsWith(fbxPath)) { - // texture path is a child of the FBX path, return the texture path without the fbx path - return texturePath.mid(fbxPath.length()); - } else { - // the texture path was not a child of the FBX path, return the empty string - return ""; - } -} - -QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { - // first make sure we have a unique base name for this texture - // in case another texture referenced by this model has the same base name - auto nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; - - QString bakedTextureFileName { textureFileInfo.completeBaseName() }; - - if (nameMatches > 0) { - // there are already nameMatches texture with this name - // append - and that number to our baked texture file name so that it is unique - bakedTextureFileName += "-" + QString::number(nameMatches); - } - - bakedTextureFileName += BAKED_TEXTURE_EXT; - - // increment the number of name matches - ++nameMatches; - - return bakedTextureFileName; -} - -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) { - QUrl urlToTexture; - - if (textureFileInfo.exists() && textureFileInfo.isFile()) { - // set the texture URL to the local texture that we have confirmed exists - urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); - } else { - // external texture that we'll need to download or find - - // first check if it the RelativePath to the texture in the FBX was relative - QString relativeFileName = fileTexture->GetRelativeFileName(); - auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); - - // this is a relative file path which will require different handling - // depending on the location of the original FBX - if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { - // the absolute path we ran into for the texture in the FBX exists on this machine - // so use that file - urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); - } else { - // we didn't find the texture on this machine at the absolute path - // so assume that it is right beside the FBX to match the behaviour of interface - urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); - } - } - - return urlToTexture; -} - -image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, FbxSurfaceMaterial* material) { - using namespace image::TextureUsage; - - // this is a property we know has a texture, we need to match it to a High Fidelity known texture type - // since that information is passed to the baking process - - // grab the hierarchical name for this property and lowercase it for case-insensitive compare - auto propertyName = QString(property.GetHierarchicalName()).toLower(); - - // figure out the type of the property based on what known value string it matches - if ((propertyName.contains("diffuse") && !propertyName.contains("tex_global_diffuse")) - || propertyName.contains("tex_color_map")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("transparentcolor") || propertyName.contains("transparencyfactor")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("bump")) { - return BUMP_TEXTURE; - } else if (propertyName.contains("normal")) { - return NORMAL_TEXTURE; - } else if ((propertyName.contains("specular") && !propertyName.contains("tex_global_specular")) - || propertyName.contains("reflection")) { - return SPECULAR_TEXTURE; - } else if (propertyName.contains("tex_metallic_map")) { - return METALLIC_TEXTURE; - } else if (propertyName.contains("shininess")) { - return GLOSS_TEXTURE; - } else if (propertyName.contains("tex_roughness_map")) { - return ROUGHNESS_TEXTURE; - } else if (propertyName.contains("emissive")) { - return EMISSIVE_TEXTURE; - } else if (propertyName.contains("ambientcolor")) { - return LIGHTMAP_TEXTURE; - } else if (propertyName.contains("ambientfactor")) { - // we need to check what the ambient factor is, since that tells Interface to process this texture - // either as an occlusion texture or a light map - auto lambertMaterial = FbxCast(material); - - if (lambertMaterial->AmbientFactor == 0) { - return LIGHTMAP_TEXTURE; - } else if (lambertMaterial->AmbientFactor > 0) { - return OCCLUSION_TEXTURE; - } else { - return UNUSED_TEXTURE; - } - - } else if (propertyName.contains("tex_ao_map")) { - return OCCLUSION_TEXTURE; - } - - return UNUSED_TEXTURE; -} - -void FBXBaker::rewriteAndBakeSceneTextures() { - - // enumerate the surface materials to find the textures used in the scene - int numMaterials = _scene->GetMaterialCount(); - for (int i = 0; i < numMaterials; i++) { - FbxSurfaceMaterial* material = _scene->GetMaterial(i); - - if (material) { - // enumerate the properties of this material to see what texture channels it might have - FbxProperty property = material->GetFirstProperty(); - - while (property.IsValid()) { - // first check if this property has connected textures, if not we don't need to bother with it here - if (property.GetSrcObjectCount() > 0) { - - // figure out the type of texture from the material property - auto textureType = textureTypeForMaterialProperty(property, material); - - if (textureType != image::TextureUsage::UNUSED_TEXTURE) { - int numTextures = property.GetSrcObjectCount(); - - for (int j = 0; j < numTextures; j++) { - FbxFileTexture* fileTexture = property.GetSrcObject(j); - - // use QFileInfo to easily split up the existing texture filename into its components - QString fbxTextureFileName { fileTexture->GetFileName() }; - QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; - - // make sure this texture points to something and isn't one we've already re-mapped - if (!textureFileInfo.filePath().isEmpty() - && textureFileInfo.suffix() != BAKED_TEXTURE_EXT.mid(1)) { - - // construct the new baked texture file name and file path - // ensuring that the baked texture will have a unique name - // even if there was another texture with the same name at a different path - auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); - QString bakedTextureFilePath { - _bakedOutputDir + "/" + bakedTextureFileName - }; - _outputFiles.push_back(bakedTextureFilePath); - - qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() - << "to" << bakedTextureFilePath; - - // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); - - // write the new filename into the FBX scene - fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); - - // write the relative filename to be the baked texture file name since it will - // be right beside the FBX - fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData()); - - if (!_bakingTextures.contains(urlToTexture)) { - // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir); - } - } - } - } - } - - property = material->GetNextProperty(property); - } - } - } -} - -void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir) { - // start a bake for this texture and add it to our list to keep track of - QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir), - &TextureBaker::deleteLater - }; - - // make sure we hear when the baking texture is done - connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture); - - // keep a shared pointer to the baking texture - _bakingTextures.insert(textureURL, bakingTexture); - - // start baking the texture on one of our available worker threads - bakingTexture->moveToThread(_textureThreadGetter()); - QMetaObject::invokeMethod(bakingTexture.data(), "bake"); -} - -void FBXBaker::handleBakedTexture() { - TextureBaker* bakedTexture = qobject_cast(sender()); - - // make sure we haven't already run into errors, and that this is a valid texture - if (bakedTexture) { - if (!hasErrors()) { - if (!bakedTexture->hasErrors()) { - if (!_originalOutputDir.isEmpty()) { - // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture - - // use the path to the texture being baked to determine if this was an embedded or a linked texture - - // it is embeddded if the texure being baked was inside the original output folder - // since that is where the FBX SDK places the .fbm folder it generates when importing the FBX - - auto originalOutputFolder = QUrl::fromLocalFile(_originalOutputDir); - - if (!originalOutputFolder.isParentOf(bakedTexture->getTextureURL())) { - // for linked textures we want to save a copy of original texture beside the original FBX - - qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); - - // check if we have a relative path to use for the texture - auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL()); - - QFile originalTextureFile { - _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() - }; - - if (relativeTexturePath.length() > 0) { - // make the folders needed by the relative path - } - - if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { - qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() - << "for" << _fbxURL; - } else { - handleError("Could not save original external texture " + originalTextureFile.fileName() - + " for " + _fbxURL.toString()); - return; - } - } - } - - - // now that this texture has been baked and handled, we can remove that TextureBaker from our hash - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } else { - // there was an error baking this texture - add it to our list of errors - _errorList.append(bakedTexture->getErrors()); - - // we don't emit finished yet so that the other textures can finish baking first - _pendingErrorEmission = true; - - // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } - } else { - // we have errors to attend to, so we don't do extra processing for this texture - // but we do need to remove that TextureBaker from our list - // and then check if we're done with all textures - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } - } -} - -void FBXBaker::exportScene() { - // setup the exporter - FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); - - // save the relative path to this FBX inside our passed output folder - - auto fileName = _fbxURL.fileName(); - auto baseName = fileName.left(fileName.lastIndexOf('.')); - auto bakedFilename = baseName + BAKED_FBX_EXTENSION; - - _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; - - bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); - - if (!exportStatus) { - // failed to initialize exporter, print an error and return - handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath - + "- error: " + exporter->GetStatus().GetErrorString()); - } - - _outputFiles.push_back(_bakedFBXFilePath); - - // export the scene - exporter->Export(_scene); - - qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; -} - - -void FBXBaker::removeEmbeddedMediaFolder() { - // now that the bake is complete, remove the embedded media folder produced by the FBX SDK when it imports an FBX - //auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); - //QDir(_bakedOutputDir + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); -} - -void FBXBaker::checkIfTexturesFinished() { - // check if we're done everything we need to do for this FBX - // and emit our finished signal if we're done - - if (_bakingTextures.isEmpty()) { - // remove the embedded media folder that the FBX SDK produces when reading the original - removeEmbeddedMediaFolder(); - - if (hasErrors()) { - // if we're checking for completion but we have errors - // that means one or more of our texture baking operations failed - - if (_pendingErrorEmission) { - emit finished(); - } - - return; - } else { - qCDebug(model_baking) << "Finished baking" << _fbxURL; - - emit finished(); - } - } -} diff --git a/tools/oven/src/FBXBaker.h b/tools/oven/src/FBXBaker.h deleted file mode 100644 index faf56ed46b..0000000000 --- a/tools/oven/src/FBXBaker.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// FBXBaker.h -// tools/oven/src -// -// Created by Stephen Birarda on 3/30/17. -// Copyright 2017 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_FBXBaker_h -#define hifi_FBXBaker_h - -#include -#include -#include -#include - -#include "Baker.h" -#include "TextureBaker.h" - -#include - -namespace fbxsdk { - class FbxManager; - class FbxProperty; - class FbxScene; - class FbxFileTexture; -} - -static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; -using FBXSDKManagerUniquePointer = std::unique_ptr>; - -using TextureBakerThreadGetter = std::function; - -class FBXBaker : public Baker { - Q_OBJECT -public: - FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir = ""); - - QUrl getFBXUrl() const { return _fbxURL; } - QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } - std::vector getOutputFiles() const { return _outputFiles; } - -public slots: - // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread - // because the Autodesk SDK will cause a crash if it is called from multiple threads - virtual void bake() override; - -signals: - void sourceCopyReadyToLoad(); - -private slots: - void bakeSourceCopy(); - void handleFBXNetworkReply(); - void handleBakedTexture(); - -private: - void setupOutputFolder(); - - void loadSourceFBX(); - - void importScene(); - void rewriteAndBakeSceneTextures(); - void exportScene(); - void removeEmbeddedMediaFolder(); - - void checkIfTexturesFinished(); - - QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture); - - void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); - - QUrl _fbxURL; - - QString _bakedFBXFilePath; - - QString _bakedOutputDir; - - // If set, the original FBX and textures will also be copied here - QString _originalOutputDir; - - QDir _tempDir; - QString _originalFBXFilePath; - - // List of baked output files, includes the FBX and textures - std::vector _outputFiles; - - static FBXSDKManagerUniquePointer _sdkManager; - fbxsdk::FbxScene* _scene { nullptr }; - - QMultiHash> _bakingTextures; - QHash _textureNameMatchCount; - - TextureBakerThreadGetter _textureThreadGetter; - - bool _pendingErrorEmission { false }; -}; - -#endif // hifi_FBXBaker_h diff --git a/tools/oven/src/TextureBaker.cpp b/tools/oven/src/TextureBaker.cpp deleted file mode 100644 index 70df511d2c..0000000000 --- a/tools/oven/src/TextureBaker.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// -// TextureBaker.cpp -// tools/oven/src -// -// Created by Stephen Birarda on 4/5/17. -// Copyright 2017 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 -#include -#include -#include - -#include -#include -#include -#include - -#include "ModelBakingLoggingCategory.h" - -#include "TextureBaker.h" - -const QString BAKED_TEXTURE_EXT = ".ktx"; - -TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) : - _textureURL(textureURL), - _textureType(textureType), - _outputDirectory(outputDirectory) -{ - // figure out the baked texture filename - auto originalFilename = textureURL.fileName(); - _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; -} - -void TextureBaker::bake() { - // once our texture is loaded, kick off a the processing - connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture); - - // first load the texture (either locally or remotely) - loadTexture(); -} - -void TextureBaker::loadTexture() { - // check if the texture is local or first needs to be downloaded - if (_textureURL.isLocalFile()) { - // load up the local file - QFile localTexture { _textureURL.toLocalFile() }; - - if (!localTexture.open(QIODevice::ReadOnly)) { - handleError("Unable to open texture " + _textureURL.toString()); - return; - } - - _originalTexture = localTexture.readAll(); - - emit originalTextureLoaded(); - } else { - // remote file, kick off a download - auto& networkAccessManager = NetworkAccessManager::getInstance(); - - QNetworkRequest networkRequest; - - // setup the request to follow re-directs and always hit the network - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - - networkRequest.setUrl(_textureURL); - - qCDebug(model_baking) << "Downloading" << _textureURL; - - // kickoff the download, wait for slot to tell us it is done - auto networkReply = networkAccessManager.get(networkRequest); - connect(networkReply, &QNetworkReply::finished, this, &TextureBaker::handleTextureNetworkReply); - } -} - -void TextureBaker::handleTextureNetworkReply() { - auto requestReply = qobject_cast(sender()); - - if (requestReply->error() == QNetworkReply::NoError) { - qCDebug(model_baking) << "Downloaded texture" << _textureURL; - - // store the original texture so it can be passed along for the bake - _originalTexture = requestReply->readAll(); - - emit originalTextureLoaded(); - } else { - // add an error to our list stating that this texture could not be downloaded - handleError("Error downloading " + _textureURL.toString() + " - " + requestReply->errorString()); - } -} - -void TextureBaker::processTexture() { - auto processedTexture = image::processImage(_originalTexture, _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType); - - if (!processedTexture) { - handleError("Could not process texture " + _textureURL.toString()); - return; - } - - // the baked textures need to have the source hash added for cache checks in Interface - // so we add that to the processed texture before handling it off to be serialized - auto hashData = QCryptographicHash::hash(_originalTexture, QCryptographicHash::Md5); - std::string hash = hashData.toHex().toStdString(); - processedTexture->setSourceHash(hash); - - auto memKTX = gpu::Texture::serialize(*processedTexture); - - if (!memKTX) { - handleError("Could not serialize " + _textureURL.toString() + " to KTX"); - return; - } - - const char* data = reinterpret_cast(memKTX->_storage->data()); - const size_t length = memKTX->_storage->size(); - - // attempt to write the baked texture to the destination file path - QFile bakedTextureFile { _outputDirectory.absoluteFilePath(_bakedTextureFileName) }; - - if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { - handleError("Could not write baked texture for " + _textureURL.toString()); - } - - qCDebug(model_baking) << "Baked texture" << _textureURL; - emit finished(); -} diff --git a/tools/oven/src/TextureBaker.h b/tools/oven/src/TextureBaker.h deleted file mode 100644 index ee1e968f20..0000000000 --- a/tools/oven/src/TextureBaker.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// TextureBaker.h -// tools/oven/src -// -// Created by Stephen Birarda on 4/5/17. -// Copyright 2017 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_TextureBaker_h -#define hifi_TextureBaker_h - -#include -#include -#include - -#include - -#include "Baker.h" - -extern const QString BAKED_TEXTURE_EXT; - -class TextureBaker : public Baker { - Q_OBJECT - -public: - TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); - - const QByteArray& getOriginalTexture() const { return _originalTexture; } - - QUrl getTextureURL() const { return _textureURL; } - - QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); } - QString getBakedTextureFileName() const { return _bakedTextureFileName; } - -public slots: - virtual void bake() override; - -signals: - void originalTextureLoaded(); - -private slots: - void processTexture(); - -private: - void loadTexture(); - void handleTextureNetworkReply(); - - QUrl _textureURL; - QByteArray _originalTexture; - image::TextureUsage::Type _textureType; - - QDir _outputDirectory; - QString _bakedTextureFileName; -}; - -#endif // hifi_TextureBaker_h From b60d68c7149487e5d824ee9ffd0705df6401c3a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 14:00:09 -0700 Subject: [PATCH 04/40] Replace FBX SDK loading of file in FBXBaker with FBXReader --- libraries/baking/CMakeLists.txt | 2 +- libraries/baking/src/FBXBaker.cpp | 28 +++++++++------------------- libraries/baking/src/FBXBaker.h | 6 +++++- tools/oven/CMakeLists.txt | 2 +- tools/oven/src/ui/BakeWidget.h | 2 +- tools/oven/src/ui/ModelBakeWidget.h | 2 +- tools/oven/src/ui/SkyboxBakeWidget.h | 2 +- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 806220ec30..da3389c862 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -15,5 +15,5 @@ if (FBX_FOUND) endif (UNIX) endif () -link_hifi_libraries(shared model networking ktx image) +link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 81b5de7546..9a28e6a788 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -1,6 +1,6 @@ // // FBXBaker.cpp -// tools/oven/src +// tools/baking/src // // Created by Stephen Birarda on 3/30/17. // Copyright 2017 High Fidelity, Inc. @@ -27,6 +27,9 @@ #include +#include +#include + #include "ModelBakingLoggingCategory.h" #include "TextureBaker.h" @@ -205,29 +208,16 @@ void FBXBaker::handleFBXNetworkReply() { } void FBXBaker::importScene() { - // create an FBX SDK importer - FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); - qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); - // import the copy of the original FBX file - bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); - if (!importStatus) { - // failed to initialize importer, print an error and return - handleError("Failed to import " + _fbxURL.toString() + " - " + importer->GetStatus().GetErrorString()); + QFile fbxFile(_originalFBXFilePath); + if (!fbxFile.open(QIODevice::ReadOnly)) { + handleError("Error opening " + _originalFBXFilePath); return; - } else { - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; } - // setup a new scene to hold the imported file - _scene = FbxScene::Create(_sdkManager.get(), "bakeScene"); - - // import the file to the created scene - importer->Import(_scene); - - // destroy the importer that is no longer needed - importer->Destroy(); + qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; + _rootNode = FBXReader::parseFBX(&fbxFile); } QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 00e7987422..0eb25f510b 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -1,6 +1,6 @@ // // FBXBaker.h -// tools/oven/src +// tools/baking/src // // Created by Stephen Birarda on 3/30/17. // Copyright 2017 High Fidelity, Inc. @@ -24,6 +24,8 @@ #include +#include + namespace fbxsdk { class FbxManager; class FbxProperty; @@ -76,6 +78,8 @@ private: void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); QUrl _fbxURL; + + FBXNode _rootNode; QString _bakedFBXFilePath; diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 0d692b5465..010b1c25b1 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME oven) setup_hifi_project(Widgets Gui Concurrent) -link_hifi_libraries(networking shared image gpu ktx fbx baking) +link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() diff --git a/tools/oven/src/ui/BakeWidget.h b/tools/oven/src/ui/BakeWidget.h index e7ab8d1840..00996128ed 100644 --- a/tools/oven/src/ui/BakeWidget.h +++ b/tools/oven/src/ui/BakeWidget.h @@ -14,7 +14,7 @@ #include -#include "../Baker.h" +#include class BakeWidget : public QWidget { Q_OBJECT diff --git a/tools/oven/src/ui/ModelBakeWidget.h b/tools/oven/src/ui/ModelBakeWidget.h index ed08990ba5..b42b8725f6 100644 --- a/tools/oven/src/ui/ModelBakeWidget.h +++ b/tools/oven/src/ui/ModelBakeWidget.h @@ -16,7 +16,7 @@ #include -#include "../FBXBaker.h" +#include #include "BakeWidget.h" diff --git a/tools/oven/src/ui/SkyboxBakeWidget.h b/tools/oven/src/ui/SkyboxBakeWidget.h index 4063a5459b..f00ab07f33 100644 --- a/tools/oven/src/ui/SkyboxBakeWidget.h +++ b/tools/oven/src/ui/SkyboxBakeWidget.h @@ -16,7 +16,7 @@ #include -#include "../TextureBaker.h" +#include #include "BakeWidget.h" From 7214f5737625d777ebdecb7bd6046e71f7d6f0bb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 14:12:15 -0700 Subject: [PATCH 05/40] Update FBXBaker::exportScene with FBXWriter --- libraries/baking/src/FBXBaker.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 9a28e6a788..35d981f337 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -212,7 +212,7 @@ void FBXBaker::importScene() { QFile fbxFile(_originalFBXFilePath); if (!fbxFile.open(QIODevice::ReadOnly)) { - handleError("Error opening " + _originalFBXFilePath); + handleError("Error opening " + _originalFBXFilePath + " for reading"); return; } @@ -502,29 +502,25 @@ void FBXBaker::handleBakedTexture() { } void FBXBaker::exportScene() { - // setup the exporter - FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); - // save the relative path to this FBX inside our passed output folder - auto fileName = _fbxURL.fileName(); auto baseName = fileName.left(fileName.lastIndexOf('.')); auto bakedFilename = baseName + BAKED_FBX_EXTENSION; _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; - bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); + auto fbxData = FBXWriter::encodeFBX(_rootNode); - if (!exportStatus) { - // failed to initialize exporter, print an error and return - handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath - + "- error: " + exporter->GetStatus().GetErrorString()); + QFile bakedFile(_bakedFBXFilePath); + + if (!bakedFile.open(QIODevice::WriteOnly)) { + handleError("Error opening " + _bakedFBXFilePath + " for writing"); + return; } - _outputFiles.push_back(_bakedFBXFilePath); + bakedFile.write(fbxData); - // export the scene - exporter->Export(_scene); + _outputFiles.push_back(_bakedFBXFilePath); qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; } From b153d1e1779aa53372a22e7e6f8a247b4cf0b675 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Sep 2017 18:38:29 -0700 Subject: [PATCH 06/40] use FBXReader/FBXWriter for texture baking in FBXBaker --- libraries/baking/src/FBXBaker.cpp | 108 ++++++++++++++--------- libraries/baking/src/FBXBaker.h | 18 ++-- libraries/baking/src/TextureBaker.cpp | 13 ++- libraries/baking/src/TextureBaker.h | 3 +- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 2 +- libraries/fbx/src/FBXReader_Material.cpp | 1 + libraries/fbx/src/FBXWriter.cpp | 6 +- 8 files changed, 87 insertions(+), 65 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 35d981f337..73209ec7cf 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -35,9 +35,6 @@ #include "FBXBaker.h" -std::once_flag onceFlag; -FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; - FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), @@ -45,12 +42,7 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGet _originalOutputDir(originalOutputDir), _textureThreadGetter(textureThreadGetter) { - std::call_once(onceFlag, [](){ - // create the static FBX SDK manager - _sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){ - manager->Destroy(); - }); - }); + } void FBXBaker::bake() { @@ -216,8 +208,12 @@ void FBXBaker::importScene() { return; } - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; - _rootNode = FBXReader::parseFBX(&fbxFile); + FBXReader reader; + + qCDebug(model_baking) << "Parsing" << _fbxURL; + _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); + _geometry = *reader.extractFBXGeometry({}, _fbxURL.toString()); + _textureContent = reader._textureContent; } QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { @@ -254,7 +250,7 @@ QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { return bakedTextureFileName; } -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) { +QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName) { QUrl urlToTexture; if (textureFileInfo.exists() && textureFileInfo.isFile()) { @@ -264,7 +260,6 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* f // external texture that we'll need to download or find // first check if it the RelativePath to the texture in the FBX was relative - QString relativeFileName = fileTexture->GetRelativeFileName(); auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); // this is a relative file path which will require different handling @@ -336,31 +331,44 @@ image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, } void FBXBaker::rewriteAndBakeSceneTextures() { + using namespace image::TextureUsage; + QHash textureTypes; - // enumerate the surface materials to find the textures used in the scene - int numMaterials = _scene->GetMaterialCount(); - for (int i = 0; i < numMaterials; i++) { - FbxSurfaceMaterial* material = _scene->GetMaterial(i); + // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID + for (const auto& material : _geometry.materials) { + if (material.normalTexture.isBumpmap) { + textureTypes[material.normalTexture.id] = BUMP_TEXTURE; + } else { + textureTypes[material.normalTexture.id] = NORMAL_TEXTURE; + } - if (material) { - // enumerate the properties of this material to see what texture channels it might have - FbxProperty property = material->GetFirstProperty(); + textureTypes[material.albedoTexture.id] = ALBEDO_TEXTURE; + textureTypes[material.glossTexture.id] = GLOSS_TEXTURE; + textureTypes[material.roughnessTexture.id] = ROUGHNESS_TEXTURE; + textureTypes[material.specularTexture.id] = SPECULAR_TEXTURE; + textureTypes[material.metallicTexture.id] = METALLIC_TEXTURE; + textureTypes[material.emissiveTexture.id] = EMISSIVE_TEXTURE; + textureTypes[material.occlusionTexture.id] = OCCLUSION_TEXTURE; + textureTypes[material.lightmapTexture.id] = LIGHTMAP_TEXTURE; + } - while (property.IsValid()) { - // first check if this property has connected textures, if not we don't need to bother with it here - if (property.GetSrcObjectCount() > 0) { + // enumerate the children of the root node + for (FBXNode& rootChild : _rootNode.children) { - // figure out the type of texture from the material property - auto textureType = textureTypeForMaterialProperty(property, material); + if (rootChild.name == "Objects") { - if (textureType != image::TextureUsage::UNUSED_TEXTURE) { - int numTextures = property.GetSrcObjectCount(); + // enumerate the objects + auto object = rootChild.children.begin(); + while (object != rootChild.children.end()) { + if (object->name == "Texture") { - for (int j = 0; j < numTextures; j++) { - FbxFileTexture* fileTexture = property.GetSrcObject(j); + // enumerate the texture children + for (FBXNode& textureChild : object->children) { + + if (textureChild.name == "RelativeFilename") { // use QFileInfo to easily split up the existing texture filename into its components - QString fbxTextureFileName { fileTexture->GetFileName() }; + QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; // make sure this texture points to something and isn't one we've already re-mapped @@ -383,38 +391,50 @@ void FBXBaker::rewriteAndBakeSceneTextures() { }; _outputFiles.push_back(bakedTextureFilePath); - qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() - << "to" << bakedTextureFilePath; + qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName + << "to" << bakedTextureFileName; // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); + auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName); // write the new filename into the FBX scene - fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); - - // write the relative filename to be the baked texture file name since it will - // be right beside the FBX - fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData()); + textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); if (!_bakingTextures.contains(urlToTexture)) { + + // grab the ID for this texture so we can figure out the + // texture type from the loaded materials + QString textureID { object->properties[0].toByteArray() }; + auto textureType = textureTypes[textureID]; + + // check if this was an embedded texture we have already have in-memory content for + auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); + // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir); + bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); } } } } - } - property = material->GetNextProperty(property); + ++object; + + } else if (object->name == "Video") { + // this is an embedded texture, we need to remove it from the FBX + object = rootChild.children.erase(object); + } else { + ++object; + } } } } } -void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir) { +void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDir, const QByteArray& textureContent) { // start a bake for this texture and add it to our list to keep track of QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir), + new TextureBaker(textureURL, textureType, outputDir, textureContent), &TextureBaker::deleteLater }; @@ -464,7 +484,7 @@ void FBXBaker::handleBakedTexture() { if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() - << "for" << _fbxURL; + << "for" << _fbxURL; } else { handleError("Could not save original external texture " + originalTextureFile.fileName() + " for " + _fbxURL.toString()); diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 0eb25f510b..2dc31828cb 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -26,15 +26,7 @@ #include -namespace fbxsdk { - class FbxManager; - class FbxProperty; - class FbxScene; - class FbxFileTexture; -} - static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; -using FBXSDKManagerUniquePointer = std::unique_ptr>; using TextureBakerThreadGetter = std::function; @@ -73,13 +65,16 @@ private: void checkIfTexturesFinished(); QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture); + QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName); - void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); + void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, + const QByteArray& textureContent = QByteArray()); QUrl _fbxURL; FBXNode _rootNode; + FBXGeometry _geometry; + QHash _textureContent; QString _bakedFBXFilePath; @@ -91,9 +86,6 @@ private: QDir _tempDir; QString _originalFBXFilePath; - static FBXSDKManagerUniquePointer _sdkManager; - fbxsdk::FbxScene* _scene { nullptr }; - QMultiHash> _bakingTextures; QHash _textureNameMatchCount; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 548e3921e4..cdf21a0290 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -25,8 +25,10 @@ const QString BAKED_TEXTURE_EXT = ".ktx"; -TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) : +TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDirectory, const QByteArray& textureContent) : _textureURL(textureURL), + _originalTexture(textureContent), _textureType(textureType), _outputDirectory(outputDirectory) { @@ -39,8 +41,13 @@ void TextureBaker::bake() { // once our texture is loaded, kick off a the processing connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture); - // first load the texture (either locally or remotely) - loadTexture(); + if (_originalTexture.isEmpty()) { + // first load the texture (either locally or remotely) + loadTexture(); + } else { + // we already have a texture passed to us, use that + emit originalTextureLoaded(); + } } const QStringList TextureBaker::getSupportedFormats() { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index 76d0a69823..5ea696dda0 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -27,7 +27,8 @@ class TextureBaker : public Baker { Q_OBJECT public: - TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); + TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDirectory, const QByteArray& textureContent = QByteArray()); static const QStringList getSupportedFormats(); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 9e1982f9e0..fc45e9a792 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -113,6 +113,7 @@ const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; /// A texture map in an FBX document. class FBXTexture { public: + QString id; QString name; QByteArray filename; QByteArray content; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d212ec820f..d4a58a1126 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -939,7 +939,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QByteArray content; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { - filepath= subobject.properties.at(0).toByteArray(); + filepath = subobject.properties.at(0).toByteArray(); filepath = filepath.replace('\\', '/'); } else if (subobject.name == "Content" && !subobject.properties.isEmpty()) { diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index ca2ec557b4..ef6496cd10 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -92,6 +92,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) { texture.filename = filepath; } + texture.id = textureID; texture.name = _textureNames.value(textureID); texture.transform.setIdentity(); texture.texcoordSet = 0; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index a084d94a46..f6bb92a8b5 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -119,7 +119,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QMetaType::QString: { - auto& bytes = prop.toString().toUtf8(); + auto bytes = prop.toString().toUtf8(); out << 'S'; out << bytes.length(); out << bytes; @@ -130,7 +130,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QMetaType::QByteArray: { - auto& bytes = prop.toByteArray(); + auto bytes = prop.toByteArray(); out.device()->write("S", 1); out << (int32_t)bytes.size(); out.writeRawData(bytes, bytes.size()); @@ -140,7 +140,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { // TODO Delete? Do we ever use QList instead of QVector? case QVariant::Type::List: { - auto& list = prop.toList(); + auto list = prop.toList(); auto listType = prop.userType(); switch (listType) { From 3cf77f377c149717545eea83fe9e988f792dd24f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Sep 2017 18:46:44 -0700 Subject: [PATCH 07/40] remove FBX SDK from CMake files --- cmake/modules/FindFBX.cmake | 111 -------------------------------- libraries/baking/CMakeLists.txt | 14 ---- tools/oven/CMakeLists.txt | 12 ---- 3 files changed, 137 deletions(-) delete mode 100644 cmake/modules/FindFBX.cmake diff --git a/cmake/modules/FindFBX.cmake b/cmake/modules/FindFBX.cmake deleted file mode 100644 index 3963f4d2e6..0000000000 --- a/cmake/modules/FindFBX.cmake +++ /dev/null @@ -1,111 +0,0 @@ -# Locate the FBX SDK -# -# Defines the following variables: -# -# FBX_FOUND - Found the FBX SDK -# FBX_VERSION - Version number -# FBX_INCLUDE_DIRS - Include directories -# FBX_LIBRARIES - The libraries to link to -# -# Accepts the following variables as input: -# -# FBX_VERSION - as a CMake variable, e.g. 2017.0.1 -# FBX_ROOT - (as a CMake or environment variable) -# The root directory of the FBX SDK install - -# adapted from https://github.com/ufz-vislab/VtkFbxConverter/blob/master/FindFBX.cmake -# which uses the MIT license (https://github.com/ufz-vislab/VtkFbxConverter/blob/master/LICENSE.txt) - -if (NOT FBX_VERSION) - set(FBX_VERSION 2017.1) -endif() - -string(REGEX REPLACE "^([0-9]+).*$" "\\1" FBX_VERSION_MAJOR "${FBX_VERSION}") -string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_MINOR "${FBX_VERSION}") -string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_PATCH "${FBX_VERSION}") - -set(FBX_MAC_LOCATIONS "/Applications/Autodesk/FBX\ SDK/${FBX_VERSION}") -set(FBX_LINUX_LOCATIONS "/usr/local/fbxsdk") - -if (WIN32) - string(REGEX REPLACE "\\\\" "/" WIN_PROGRAM_FILES_X64_DIRECTORY $ENV{ProgramW6432}) -endif() - -set(FBX_WIN_LOCATIONS "${WIN_PROGRAM_FILES_X64_DIRECTORY}/Autodesk/FBX/FBX SDK/${FBX_VERSION}") - -set(FBX_SEARCH_LOCATIONS $ENV{FBX_ROOT} ${FBX_ROOT} ${FBX_MAC_LOCATIONS} ${FBX_WIN_LOCATIONS} ${FBX_LINUX_LOCATIONS}) - -function(_fbx_append_debugs _endvar _library) - if (${_library} AND ${_library}_DEBUG) - set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) - else() - set(_output ${${_library}}) - endif() - - set(${_endvar} ${_output} PARENT_SCOPE) -endfunction() - -if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - set(fbx_compiler clang) -elseif (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") - set(fbx_compiler gcc4) -endif() - -function(_fbx_find_library _name _lib _suffix) - if (MSVC_VERSION EQUAL 1910) - set(VS_PREFIX vs2015) - elseif (MSVC_VERSION EQUAL 1900) - set(VS_PREFIX vs2015) - elseif (MSVC_VERSION EQUAL 1800) - set(VS_PREFIX vs2013) - elseif (MSVC_VERSION EQUAL 1700) - set(VS_PREFIX vs2012) - elseif (MSVC_VERSION EQUAL 1600) - set(VS_PREFIX vs2010) - elseif (MSVC_VERSION EQUAL 1500) - set(VS_PREFIX vs2008) - endif() - - find_library(${_name} - NAMES ${_lib} - HINTS ${FBX_SEARCH_LOCATIONS} - PATH_SUFFIXES lib/${fbx_compiler}/${_suffix} lib/${fbx_compiler}/x64/${_suffix} lib/${fbx_compiler}/ub/${_suffix} lib/${VS_PREFIX}/x64/${_suffix} - ) - - mark_as_advanced(${_name}) -endfunction() - -find_path(FBX_INCLUDE_DIR fbxsdk.h - PATHS ${FBX_SEARCH_LOCATIONS} - PATH_SUFFIXES include -) -mark_as_advanced(FBX_INCLUDE_DIR) - -if (WIN32) - _fbx_find_library(FBX_LIBRARY libfbxsdk-md release) - _fbx_find_library(FBX_LIBRARY_DEBUG libfbxsdk-md debug) -elseif (APPLE) - find_library(CARBON NAMES Carbon) - find_library(SYSTEM_CONFIGURATION NAMES SystemConfiguration) - _fbx_find_library(FBX_LIBRARY libfbxsdk.a release) - _fbx_find_library(FBX_LIBRARY_DEBUG libfbxsdk.a debug) -else () - _fbx_find_library(FBX_LIBRARY libfbxsdk.a release) -endif() - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(FBX DEFAULT_MSG FBX_LIBRARY FBX_INCLUDE_DIR) - -if (FBX_FOUND) - set(FBX_INCLUDE_DIRS ${FBX_INCLUDE_DIR}) - _fbx_append_debugs(FBX_LIBRARIES FBX_LIBRARY) - add_definitions(-DFBXSDK_NEW_API) - - if (WIN32) - add_definitions(-DK_PLUGIN -DK_FBXSDK -DK_NODLL) - set(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:\"LIBCMT\") - set(FBX_LIBRARIES ${FBX_LIBRARIES} Wininet.lib) - elseif (APPLE) - set(FBX_LIBRARIES ${FBX_LIBRARIES} ${CARBON} ${SYSTEM_CONFIGURATION}) - endif() -endif() diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index da3389c862..0805c0198c 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -1,19 +1,5 @@ set(TARGET_NAME baking) setup_hifi_library(Concurrent) -find_package(FBX) -if (FBX_FOUND) - if (CMAKE_THREAD_LIBS_INIT) - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") - else () - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) - endif () - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) - - if (UNIX) - target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) - endif (UNIX) -endif () - link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 010b1c25b1..1022c204c5 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -17,16 +17,4 @@ if (UNIX) endif() endif () -# try to find the FBX SDK but fail silently if we don't -# because this tool is not built by default -find_package(FBX) -if (FBX_FOUND) - if (CMAKE_THREAD_LIBS_INIT) - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") - else () - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) - endif () - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) -endif () - set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) From 0d7b50cfb66370ddcca5b379b16f70430a92a573 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 8 Sep 2017 09:44:19 -0700 Subject: [PATCH 08/40] remove old texture type method --- libraries/baking/src/FBXBaker.cpp | 52 ------------------------------- 1 file changed, 52 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 73209ec7cf..e3b1269d37 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -278,58 +278,6 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF return urlToTexture; } -image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, FbxSurfaceMaterial* material) { - using namespace image::TextureUsage; - - // this is a property we know has a texture, we need to match it to a High Fidelity known texture type - // since that information is passed to the baking process - - // grab the hierarchical name for this property and lowercase it for case-insensitive compare - auto propertyName = QString(property.GetHierarchicalName()).toLower(); - - // figure out the type of the property based on what known value string it matches - if ((propertyName.contains("diffuse") && !propertyName.contains("tex_global_diffuse")) - || propertyName.contains("tex_color_map")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("transparentcolor") || propertyName.contains("transparencyfactor")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("bump")) { - return BUMP_TEXTURE; - } else if (propertyName.contains("normal")) { - return NORMAL_TEXTURE; - } else if ((propertyName.contains("specular") && !propertyName.contains("tex_global_specular")) - || propertyName.contains("reflection")) { - return SPECULAR_TEXTURE; - } else if (propertyName.contains("tex_metallic_map")) { - return METALLIC_TEXTURE; - } else if (propertyName.contains("shininess")) { - return GLOSS_TEXTURE; - } else if (propertyName.contains("tex_roughness_map")) { - return ROUGHNESS_TEXTURE; - } else if (propertyName.contains("emissive")) { - return EMISSIVE_TEXTURE; - } else if (propertyName.contains("ambientcolor")) { - return LIGHTMAP_TEXTURE; - } else if (propertyName.contains("ambientfactor")) { - // we need to check what the ambient factor is, since that tells Interface to process this texture - // either as an occlusion texture or a light map - auto lambertMaterial = FbxCast(material); - - if (lambertMaterial->AmbientFactor == 0) { - return LIGHTMAP_TEXTURE; - } else if (lambertMaterial->AmbientFactor > 0) { - return OCCLUSION_TEXTURE; - } else { - return UNUSED_TEXTURE; - } - - } else if (propertyName.contains("tex_ao_map")) { - return OCCLUSION_TEXTURE; - } - - return UNUSED_TEXTURE; -} - void FBXBaker::rewriteAndBakeSceneTextures() { using namespace image::TextureUsage; QHash textureTypes; From 91f455159808cbfc3b5b953b622f3048f22ee121 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 8 Aug 2017 11:24:16 -0700 Subject: [PATCH 09/40] add draco to the oven tool --- cmake/externals/draco/CMakeLists.txt | 35 ++++++++++++++++++++++++++++ cmake/modules/FindDraco.cmake | 30 ++++++++++++++++++++++++ tools/oven/CMakeLists.txt | 5 ++++ 3 files changed, 70 insertions(+) create mode 100644 cmake/externals/draco/CMakeLists.txt create mode 100644 cmake/modules/FindDraco.cmake diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt new file mode 100644 index 0000000000..8ef1593dc1 --- /dev/null +++ b/cmake/externals/draco/CMakeLists.txt @@ -0,0 +1,35 @@ +set(EXTERNAL_NAME draco) + +if (ANDROID) + set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") +endif () + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-master-c8b6219.zip + URL_MD5 d60ed5fb2e1792445078f931875e3ee1 + CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}draco.${LIB_EXT} CACHE FILEPATH "Path to Draco release library") +set(${EXTERNAL_NAME_UPPER}_ENCODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracoenc.${LIB_EXT} CACHE FILEPATH "Path to Draco encoder release library") +set(${EXTERNAL_NAME_UPPER}_DECODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracodec.${LIB_EXT} CACHE FILEPATH "Path to Draco decoder release library") diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake new file mode 100644 index 0000000000..f549d410f9 --- /dev/null +++ b/cmake/modules/FindDraco.cmake @@ -0,0 +1,30 @@ +# +# FindDraco.cmake +# +# Try to find Draco libraries and include path. +# Once done this will define +# +# DRACO_FOUND +# DRACO_INCLUDE_DIRS +# DRACO_LIBRARY +# DRACO_ENCODER_LIBRARY +# DRACO_DECODER_LIBRARY +# +# Created on 8/8/2017 by Stephen Birarda +# Copyright 2017 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("draco") + +find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src HINTS ${DRACO_SEARCH_DIRS}) + +find_library(DRACO_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) +find_library(DRACO_ENCODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) +find_library(DRACO_DECODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DRACO DEFAULT_MSG DRACO_INCLUDE_DIRS DRACO_LIBRARY DRACO_ENCODER_LIBRARY DRACO_DECODER_LIBRARY) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 1022c204c5..9d5d6c1aad 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -6,6 +6,11 @@ link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) + if (WIN32) package_libraries_for_deployment() endif () From 09d18b5ba91fb67774a759acf92cda0d93acd0fe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 8 Aug 2017 12:17:27 -0700 Subject: [PATCH 10/40] handle special include path for Draco on WIN --- cmake/externals/draco/CMakeLists.txt | 7 ++++++- cmake/modules/FindDraco.cmake | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 8ef1593dc1..03a6622a1b 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -21,7 +21,12 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") + +if (UNIX) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") +else () + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") +endif () if (UNIX) set(LIB_PREFIX "lib") diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake index f549d410f9..342797b62e 100644 --- a/cmake/modules/FindDraco.cmake +++ b/cmake/modules/FindDraco.cmake @@ -20,7 +20,7 @@ include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("draco") -find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src HINTS ${DRACO_SEARCH_DIRS}) +find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src include HINTS ${DRACO_SEARCH_DIRS}) find_library(DRACO_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) find_library(DRACO_ENCODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) From 9b462171f623b28cadf1c34d4f42bac3521eaaed Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Sep 2017 16:56:23 -0700 Subject: [PATCH 11/40] Add draco support to FBXBaker --- libraries/baking/CMakeLists.txt | 5 + libraries/baking/src/FBXBaker.cpp | 136 ++++++++++++++++++++++++++- libraries/baking/src/FBXBaker.h | 3 +- libraries/fbx/src/FBX.h | 7 ++ libraries/fbx/src/FBXReader.h | 2 +- libraries/fbx/src/FBXReader_Mesh.cpp | 3 +- tools/oven/CMakeLists.txt | 5 - 7 files changed, 148 insertions(+), 13 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 0805c0198c..66cf791776 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -3,3 +3,8 @@ setup_hifi_library(Concurrent) link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) + +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index e3b1269d37..f504b3f03c 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -11,8 +11,6 @@ #include // need this include so we don't get an error looking for std::isnan -#include - #include #include #include @@ -35,6 +33,9 @@ #include "FBXBaker.h" +#include +#include + FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), @@ -80,7 +81,8 @@ void FBXBaker::bakeSourceCopy() { return; } - // enumerate the textures found in the scene and start a bake for them + // enumerate the models and textures found in the scene and start a bake for them + rewriteAndBakeSceneModels(); rewriteAndBakeSceneTextures(); if (hasErrors()) { @@ -212,7 +214,7 @@ void FBXBaker::importScene() { qCDebug(model_baking) << "Parsing" << _fbxURL; _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); - _geometry = *reader.extractFBXGeometry({}, _fbxURL.toString()); + _geometry = reader.extractFBXGeometry({}, _fbxURL.toString()); _textureContent = reader._textureContent; } @@ -278,12 +280,136 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF return urlToTexture; } +void FBXBaker::rewriteAndBakeSceneModels() { + unsigned int meshIndex = 0; + for (FBXNode& rootChild : _rootNode.children) { + if (rootChild.name == "Objects") { + for (FBXNode& objectChild : rootChild.children) { + if (objectChild.name == "Geometry") { + + // TODO Pull this out of _geometry instead so we don't have to reprocess it + auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); + auto mesh = extractedMesh.mesh; + + Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); + Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); + Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); + + int64_t numTriangles { 0 }; + for (auto& part : mesh.parts) { + Q_ASSERT(part.quadTrianglesIndices.size() % 3 == 0); + Q_ASSERT(part.triangleIndices.size() % 3 == 0); + + numTriangles += part.quadTrianglesIndices.size() / 3; + numTriangles += part.triangleIndices.size() / 3; + } + + draco::TriangleSoupMeshBuilder meshBuilder; + + meshBuilder.Start(numTriangles); + + bool hasNormals { mesh.normals.size() > 0 }; + bool hasColors { mesh.colors.size() > 0 }; + bool hasTexCoords { mesh.texCoords.size() > 0 }; + //bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + + int normalsAttributeID { -1 }; + int colorsAttributeID { -1 }; + int texCoordsAttributeID { -1 }; + //int texCoords1AttributeID { -1 }; + + const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, + 3, draco::DT_FLOAT32); + + const int faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 1, draco::DT_INT64); + + if (hasNormals) { + normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, + 3, draco::DT_FLOAT32); + } + if (hasColors) { + colorsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::COLOR, + 3, draco::DT_FLOAT32); + } + if (hasTexCoords) { + texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, + 2, draco::DT_FLOAT32); + } + + + for (auto& part : mesh.parts) { + //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); + //Q_ASSERT(part.triangleIndices % 3 == 0); + + int64_t materialID = 0; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + auto idx0 = part.quadTrianglesIndices[i]; + auto idx1 = part.quadTrianglesIndices[i + 1]; + auto idx2 = part.quadTrianglesIndices[i + 2]; + + auto face = draco::FaceIndex(i / 3); + + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + + meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, + &mesh.vertices[idx0], &mesh.vertices[idx1], + &mesh.vertices[idx2]); + + if (hasNormals) { + meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, + &mesh.normals[idx0],&mesh.normals[idx1], + &mesh.normals[idx2]); + } + if (hasColors) { + meshBuilder.SetAttributeValuesForFace(colorsAttributeID, face, + &mesh.colors[idx0], &mesh.colors[idx1], + &mesh.colors[idx2]); + } + if (hasTexCoords) { + meshBuilder.SetAttributeValuesForFace(texCoordsAttributeID, face, + &mesh.texCoords[idx0], &mesh.texCoords[idx1], + &mesh.texCoords[idx2]); + } + } + } + + auto dracoMesh = meshBuilder.Finalize(); + + draco::Encoder encoder; + draco::EncoderBuffer buffer; + encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); + + FBXNode dracoMeshNode; + dracoMeshNode.name = "DracoMesh"; + auto value = QVariant::fromValue(QByteArray(buffer.data(), buffer.size())); + dracoMeshNode.properties.append(value); + + + QFile file("C:/Users/huffm/encodedFBX/" + this->_fbxURL.fileName() + "-" + QString::number(meshIndex) + ".drc"); + if (file.open(QIODevice::WriteOnly)) { + file.write(buffer.data(), buffer.size()); + file.close(); + } else { + qWarning() << "Failed to write to: " << file.fileName(); + + } + + objectChild.children.push_back(dracoMeshNode); + } + } + } + } +} + void FBXBaker::rewriteAndBakeSceneTextures() { using namespace image::TextureUsage; QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _geometry.materials) { + for (const auto& material : _geometry->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 2dc31828cb..26c1ff2dcc 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -58,6 +58,7 @@ private: void loadSourceFBX(); void importScene(); + void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); void exportScene(); void removeEmbeddedMediaFolder(); @@ -73,7 +74,7 @@ private: QUrl _fbxURL; FBXNode _rootNode; - FBXGeometry _geometry; + FBXGeometry* _geometry; QHash _textureContent; QString _bakedFBXFilePath; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fc45e9a792..9f5fad7d66 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -32,6 +32,13 @@ static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; static const quint32 FBX_VERSION_2016 = 7500; + +// TODO Convert to GeometryAttribute type +static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000; +static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; +static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1; + + class FBXNode; using FBXNodeList = QList; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a600ac6bab..8a5e394da9 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -110,7 +110,7 @@ public: FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); - ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); + static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 4e153dfe3a..14f1c09b75 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -304,8 +304,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn if (subdata.name == "Materials") { materials = getIntVector(subdata); } else if (subdata.name == "MappingInformationType") { - if (subdata.properties.at(0) == BY_POLYGON) + if (subdata.properties.at(0) == BY_POLYGON) { isMaterialPerPolygon = true; + } } else { isMaterialPerPolygon = false; } diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 9d5d6c1aad..1022c204c5 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -6,11 +6,6 @@ link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() -add_dependency_external_projects(draco) -find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) - if (WIN32) package_libraries_for_deployment() endif () From 3d6d383a150e3eb92287e0b0e120c2b384677337 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Sep 2017 16:58:57 -0700 Subject: [PATCH 12/40] Clean up duplicate list code in encodeFBXProperty --- libraries/fbx/src/FBXWriter.cpp | 57 ++++++++++----------------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index f6bb92a8b5..9f389fca88 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -13,6 +13,17 @@ #include +template +void writeVector(QDataStream& out, char ch, QVector list) { + out.device()->write(&ch, 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } +} + QByteArray FBXWriter::encodeFBX(const FBXNode& root) { QByteArray data; @@ -93,6 +104,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QVariant::Type::Bool: out.device()->write("C", 1); + //out.device()->write(prop.toBool() ? 1 : 0, 1); out << prop.toBool(); break; @@ -200,50 +212,15 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { default: { if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("f", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'f', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("d", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'd', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("l", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'l', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("i", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'i', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("b", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'b', prop.value>()); } else { qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop; } From ebd925b966ecdb56d34401f7604651c9263d69e0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Sep 2017 10:52:59 -0700 Subject: [PATCH 13/40] Add proper per face materials and texCoords1 to fbx baking --- libraries/baking/src/FBXBaker.cpp | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index f504b3f03c..b5cd94cec7 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -311,20 +311,18 @@ void FBXBaker::rewriteAndBakeSceneModels() { bool hasNormals { mesh.normals.size() > 0 }; bool hasColors { mesh.colors.size() > 0 }; bool hasTexCoords { mesh.texCoords.size() > 0 }; - //bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + bool hasPerFaceMaterials { mesh.parts.size() > 0 }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; int texCoordsAttributeID { -1 }; - //int texCoords1AttributeID { -1 }; + int texCoords1AttributeID { -1 }; + int faceMaterialAttributeID { -1 }; const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32); - const int faceMaterialAttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, - 1, draco::DT_INT64); - if (hasNormals) { normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, 3, draco::DT_FLOAT32); @@ -337,13 +335,25 @@ void FBXBaker::rewriteAndBakeSceneModels() { texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32); } + if (hasTexCoords1) { + texCoords1AttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_TEX_COORD_1, + 2, draco::DT_FLOAT32); + } + if (hasPerFaceMaterials) { + faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 2, draco::DT_INT64); + } + auto partIndex = 0; for (auto& part : mesh.parts) { //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); //Q_ASSERT(part.triangleIndices % 3 == 0); - int64_t materialID = 0; + const auto matTex = extractedMesh.partMaterialTextures[partIndex]; + const int64_t matTexData[2] = { matTex.first, matTex.second }; for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { auto idx0 = part.quadTrianglesIndices[i]; @@ -352,7 +362,9 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto face = draco::FaceIndex(i / 3); - meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + if (hasPerFaceMaterials) { + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); + } meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, &mesh.vertices[idx0], &mesh.vertices[idx1], @@ -373,7 +385,14 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.texCoords[idx0], &mesh.texCoords[idx1], &mesh.texCoords[idx2]); } + if (hasTexCoords1) { + meshBuilder.SetAttributeValuesForFace(texCoords1AttributeID, face, + &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], + &mesh.texCoords1[idx2]); + } } + + partIndex++; } auto dracoMesh = meshBuilder.Finalize(); From b1d0df06d98b4348d37271e11f353a4641b2de39 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Sep 2017 12:07:41 -0700 Subject: [PATCH 14/40] Add error handling in FBXBaker for empty nodes and failed draco compression --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b5cd94cec7..30c9510a52 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -304,6 +304,11 @@ void FBXBaker::rewriteAndBakeSceneModels() { numTriangles += part.triangleIndices.size() / 3; } + if (numTriangles == 0) { + qDebug() << "Skipping compression of mesh because no triangles were found"; + continue; + } + draco::TriangleSoupMeshBuilder meshBuilder; meshBuilder.Start(numTriangles); @@ -397,6 +402,11 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto dracoMesh = meshBuilder.Finalize(); + if (!dracoMesh) { + qWarning() << "Failed to finalize the baking of a draco Geometry node"; + continue; + } + draco::Encoder encoder; draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 1b2ba3acb686792a2a34a07a1579c98f80eaed4b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 12:07:12 -0700 Subject: [PATCH 15/40] force libc++ for OS X draco build --- cmake/externals/draco/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 03a6622a1b..41af6646db 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -4,12 +4,14 @@ if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () +set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) + include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/draco-master-c8b6219.zip - URL_MD5 d60ed5fb2e1792445078f931875e3ee1 - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.0.1.zip + URL_MD5 f1826d5ba1ffd413311d78346f4c114b + CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= ${EXTRA_CMAKE_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 @@ -22,7 +24,7 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (UNIX) +if (UNIX AND NOT APPLE) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") else () set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") From 8fc8b8100d31bff229560ee7efbbef6b3c494c5c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:44:44 -0700 Subject: [PATCH 16/40] Fix FBXBaker failing on bad mesh data and draco meshes that can't be encoded --- libraries/baking/src/FBXBaker.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 30c9510a52..45f3836dfc 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -297,15 +297,16 @@ void FBXBaker::rewriteAndBakeSceneModels() { int64_t numTriangles { 0 }; for (auto& part : mesh.parts) { - Q_ASSERT(part.quadTrianglesIndices.size() % 3 == 0); - Q_ASSERT(part.triangleIndices.size() % 3 == 0); - + if ((part.quadTrianglesIndices.size() % 3) != 0 || (part.triangleIndices.size() % 3) != 0) { + handleWarning("Found a mesh part with invalid index data, skipping"); + continue; + } numTriangles += part.quadTrianglesIndices.size() / 3; numTriangles += part.triangleIndices.size() / 3; } if (numTriangles == 0) { - qDebug() << "Skipping compression of mesh because no triangles were found"; + handleWarning("Skipping compression of mesh because no triangles were found"); continue; } @@ -317,7 +318,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { bool hasColors { mesh.colors.size() > 0 }; bool hasTexCoords { mesh.texCoords.size() > 0 }; bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; - bool hasPerFaceMaterials { mesh.parts.size() > 0 }; + bool hasPerFaceMaterials { mesh.parts.size() > 1 }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; @@ -403,7 +404,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto dracoMesh = meshBuilder.Finalize(); if (!dracoMesh) { - qWarning() << "Failed to finalize the baking of a draco Geometry node"; + handleWarning("Failed to finalize the baking of a draco Geometry node"); continue; } From 2d9d4322215e3953fa8c66296699a0cf5b3018f4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:45:24 -0700 Subject: [PATCH 17/40] Update FBXBaker to remove unused nodes from the Geometry node --- libraries/baking/src/FBXBaker.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 45f3836dfc..760141f029 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -428,6 +428,34 @@ void FBXBaker::rewriteAndBakeSceneModels() { } 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", + + // 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; + } + } } } } From 9243cf75907677f7a92b39c8f4c1aea4e5d659c0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:45:46 -0700 Subject: [PATCH 18/40] Remove extraneous logging in FBXWriter --- libraries/fbx/src/FBXWriter.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 9f389fca88..c14e383402 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -63,12 +63,6 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { out << (quint8)node.name.size(); out.writeRawData(node.name, node.name.size()); - if (node.name == "Vertices") { - for (auto& prop : node.properties) { - qDebug() << "Properties: " << prop; - } - } - auto nodePropertiesStartPos = device->pos(); for (const auto& prop : node.properties) { From f9cc82c992a3703d8e51ff2ebbb56154a741b2e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 18:09:51 -0700 Subject: [PATCH 19/40] use draco 1.1.0 in external --- cmake/externals/draco/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 41af6646db..07ce861820 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -9,8 +9,8 @@ set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS= include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.0.1.zip - URL_MD5 f1826d5ba1ffd413311d78346f4c114b + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip + URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= ${EXTRA_CMAKE_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From 2105f2da9210bb4206c95c235e38184bd95e364d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 18:21:44 -0700 Subject: [PATCH 20/40] change unique ID for material and second tex coord --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 760141f029..18e7dbbd76 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -408,6 +408,16 @@ void FBXBaker::rewriteAndBakeSceneModels() { continue; } + // we need to modify unique attribute IDs for custom attributes + // so the attributes are easily retrievable on the other side + if (hasPerFaceMaterials) { + dracoMesh->attribute(faceMaterialAttributeID)->set_unique_id(DRACO_ATTRIBUTE_MATERIAL_ID); + } + + if (hasTexCoords1) { + dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); + } + draco::Encoder encoder; draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 61314949ec1fa45dc5868f102a1445a71d21facb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 12:54:45 -0700 Subject: [PATCH 21/40] add draco as external to FBX library --- libraries/fbx/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 3355ccd06e..7cead5aa4f 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,7 +1,10 @@ set(TARGET_NAME fbx) setup_hifi_library() - - link_hifi_libraries(shared model networking image) include_hifi_library_headers(gpu image) + +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) From cf282dd3fc992d5f311c98294b6249ef634131a3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 13:42:45 -0700 Subject: [PATCH 22/40] add code to read draco mesh from FBX --- libraries/fbx/src/FBXReader_Mesh.cpp | 236 ++++++++++++++++++++------- 1 file changed, 178 insertions(+), 58 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 14f1c09b75..4af68b8878 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -168,11 +170,17 @@ void appendIndex(MeshData& data, QVector& indices, int index) { ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex) { MeshData data; data.extracted.mesh.meshIndex = meshIndex++; + QVector materials; QVector textures; + bool isMaterialPerPolygon = false; + static const QVariant BY_VERTICE = QByteArray("ByVertice"); static const QVariant INDEX_TO_DIRECT = QByteArray("IndexToDirect"); + + bool isDracoMesh = false; + foreach (const FBXNode& child, object.children) { if (child.name == "Vertices") { data.vertices = createVec3Vector(getDoubleVector(child)); @@ -319,70 +327,182 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn textures = getIntVector(subdata); } } - } - } + } else if (child.name == "DracoMesh") { + isDracoMesh = true; - bool isMultiMaterial = false; - if (isMaterialPerPolygon) { - isMultiMaterial = true; - } - // TODO: make excellent use of isMultiMaterial - Q_UNUSED(isMultiMaterial); + // load the draco mesh from the FBX and create a draco::Mesh + draco::Decoder decoder; + draco::DecoderBuffer decodedBuffer; + QByteArray dracoArray = child.properties.at(0).value(); + decodedBuffer.Init(dracoArray.data(), dracoArray.size()); - // convert the polygons to quads and triangles - int polygonIndex = 0; - QHash, int> materialTextureParts; - for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) { - int endIndex = beginIndex; - while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0); + std::unique_ptr dracoMesh(new draco::Mesh()); + decoder.DecodeBufferToGeometry(&decodedBuffer, dracoMesh.get()); - QPair materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0, - (polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0); - int& partIndex = materialTextureParts[materialTexture]; - if (partIndex == 0) { - data.extracted.partMaterialTextures.append(materialTexture); - data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); - partIndex = data.extracted.mesh.parts.size(); - } - FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; - - if (endIndex - beginIndex == 4) { - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); + // read positions from draco mesh to extracted mesh + auto positionAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::POSITION); + if (positionAttribute) { + std::array positionValue; - int quadStartIndex = part.quadIndices.size() - 4; - int i0 = part.quadIndices[quadStartIndex + 0]; - int i1 = part.quadIndices[quadStartIndex + 1]; - int i2 = part.quadIndices[quadStartIndex + 2]; - int i3 = part.quadIndices[quadStartIndex + 3]; - - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - part.quadTrianglesIndices.append(i0); - part.quadTrianglesIndices.append(i1); - part.quadTrianglesIndices.append(i3); - - part.quadTrianglesIndices.append(i1); - part.quadTrianglesIndices.append(i2); - part.quadTrianglesIndices.append(i3); - - } else { - for (int nextIndex = beginIndex + 1;; ) { - appendIndex(data, part.triangleIndices, beginIndex); - appendIndex(data, part.triangleIndices, nextIndex++); - appendIndex(data, part.triangleIndices, nextIndex); - if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { - break; + for (draco::AttributeValueIndex i (0); i < positionAttribute->size(); ++i) { + positionAttribute->ConvertValue(i, &positionValue[0]); + data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); } } - beginIndex = endIndex; + + // enumerate the faces from draco mesh to collect vertex indices + for (int i = 0; i < dracoMesh->num_faces() * 3; ++i) { + auto vertexIndex = dracoMesh->face(draco::FaceIndex(i / 3))[i % 3]; + auto mappedIndex = positionAttribute->mapped_index(vertexIndex).value(); + data.extracted.newIndices.insert(mappedIndex, mappedIndex); + } + + // read normals from draco mesh to extracted mesh + auto normalAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL); + if (normalAttribute) { + std::array normalValue; + + for (draco::AttributeValueIndex i (0); i < normalAttribute->size(); ++i) { + normalAttribute->ConvertValue(i, &normalValue[0]); + data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + } + } + + // read UVs from draco mesh to extracted mesh + auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD); + if (texCoordAttribute) { + std::array texCoordValue; + + for (draco::AttributeValueIndex i (0); i < texCoordAttribute->size(); ++i) { + texCoordAttribute->ConvertValue(i, &texCoordValue[0]); + data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + } + } + + // some meshes have a second set of UVs, read those to extracted mesh + auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); + if (extraTexCoordAttribute) { + std::array texCoordValue; + + for (draco::AttributeValueIndex i (0); i < extraTexCoordAttribute->size(); ++i) { + extraTexCoordAttribute->ConvertValue(i, &texCoordValue[0]); + data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + } + } + + // read vertex colors from draco mesh to extracted mesh + auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); + if (colorAttribute) { + std::array colorValue; + + for (draco::AttributeValueIndex i (0); i < colorAttribute->size(); ++i) { + colorAttribute->ConvertValue(i, &colorValue[0]); + data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + } + } + + // read material ID and texture ID mappings into materials and texture vectors + auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); + if (matTexAttribute) { + std::array matTexValue; + + for (draco::AttributeValueIndex i (0); i < matTexAttribute->size(); ++i) { + matTexAttribute->ConvertValue(i, &matTexValue[0]); + materials.append(matTexValue[0]); + textures.append(matTexValue[1]); + } + } + + // enumerate the faces and construct the extracted mesh + auto vertexIndices = data.extracted.newIndices.keys(); + QHash, int> materialTextureParts; + + for (auto i = 0; i < vertexIndices.size(); i += 3) { + // grab the material ID and texture ID for this face, if we have it + QPair materialTexture(materials.at(i), textures.at(i)); + + // grab or setup the FBXMeshPart for the part this face belongs to + int& partIndex = materialTextureParts[materialTexture]; + if (partIndex == 0) { + data.extracted.partMaterialTextures.append(materialTexture); + data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); + partIndex = data.extracted.mesh.parts.size() - 1; + } + + FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + + // give the mesh part its indices + part.triangleIndices.append({ vertexIndices[i], vertexIndices[i + 1], vertexIndices[i + 2]}); + } + + } + } + + // when we have a draco mesh, we've already built the extracted mesh, so we don't need to do the + // processing we do for normal meshes below + if (!isDracoMesh) { + bool isMultiMaterial = false; + if (isMaterialPerPolygon) { + isMultiMaterial = true; + } + // TODO: make excellent use of isMultiMaterial + Q_UNUSED(isMultiMaterial); + + // convert the polygons to quads and triangles + int polygonIndex = 0; + QHash, int> materialTextureParts; + for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) { + int endIndex = beginIndex; + while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0); + + QPair materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0, + (polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0); + int& partIndex = materialTextureParts[materialTexture]; + if (partIndex == 0) { + data.extracted.partMaterialTextures.append(materialTexture); + data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); + partIndex = data.extracted.mesh.parts.size(); + } + FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; + + if (endIndex - beginIndex == 4) { + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + + int quadStartIndex = part.quadIndices.size() - 4; + int i0 = part.quadIndices[quadStartIndex + 0]; + int i1 = part.quadIndices[quadStartIndex + 1]; + int i2 = part.quadIndices[quadStartIndex + 2]; + int i3 = part.quadIndices[quadStartIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + part.quadTrianglesIndices.append(i0); + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i3); + + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i2); + part.quadTrianglesIndices.append(i3); + + } else { + for (int nextIndex = beginIndex + 1;; ) { + appendIndex(data, part.triangleIndices, beginIndex); + appendIndex(data, part.triangleIndices, nextIndex++); + appendIndex(data, part.triangleIndices, nextIndex); + if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { + break; + } + } + beginIndex = endIndex; + } } } From 5d9ed7836915b240e289ad6bccd9f249c830819c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 15:57:00 -0700 Subject: [PATCH 23/40] fixes for extracted mesh construction --- libraries/fbx/src/FBXReader_Mesh.cpp | 145 +++++++++++++-------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 4af68b8878..2d4e722227 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -339,87 +339,86 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::unique_ptr dracoMesh(new draco::Mesh()); decoder.DecodeBufferToGeometry(&decodedBuffer, dracoMesh.get()); - // read positions from draco mesh to extracted mesh + // prepare attributes for this mesh auto positionAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::POSITION); - if (positionAttribute) { - std::array positionValue; - - for (draco::AttributeValueIndex i (0); i < positionAttribute->size(); ++i) { - positionAttribute->ConvertValue(i, &positionValue[0]); - data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); - } - } - - // enumerate the faces from draco mesh to collect vertex indices - for (int i = 0; i < dracoMesh->num_faces() * 3; ++i) { - auto vertexIndex = dracoMesh->face(draco::FaceIndex(i / 3))[i % 3]; - auto mappedIndex = positionAttribute->mapped_index(vertexIndex).value(); - data.extracted.newIndices.insert(mappedIndex, mappedIndex); - } - - // read normals from draco mesh to extracted mesh auto normalAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL); - if (normalAttribute) { - std::array normalValue; - - for (draco::AttributeValueIndex i (0); i < normalAttribute->size(); ++i) { - normalAttribute->ConvertValue(i, &normalValue[0]); - data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); - } - } - - // read UVs from draco mesh to extracted mesh auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD); - if (texCoordAttribute) { - std::array texCoordValue; - - for (draco::AttributeValueIndex i (0); i < texCoordAttribute->size(); ++i) { - texCoordAttribute->ConvertValue(i, &texCoordValue[0]); - data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); - } - } - - // some meshes have a second set of UVs, read those to extracted mesh auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); - if (extraTexCoordAttribute) { - std::array texCoordValue; - - for (draco::AttributeValueIndex i (0); i < extraTexCoordAttribute->size(); ++i) { - extraTexCoordAttribute->ConvertValue(i, &texCoordValue[0]); - data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); - } - } - - // read vertex colors from draco mesh to extracted mesh auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); - if (colorAttribute) { - std::array colorValue; - - for (draco::AttributeValueIndex i (0); i < colorAttribute->size(); ++i) { - colorAttribute->ConvertValue(i, &colorValue[0]); - data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); - } - } - - // read material ID and texture ID mappings into materials and texture vectors auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); - if (matTexAttribute) { - std::array matTexValue; - for (draco::AttributeValueIndex i (0); i < matTexAttribute->size(); ++i) { - matTexAttribute->ConvertValue(i, &matTexValue[0]); - materials.append(matTexValue[0]); - textures.append(matTexValue[1]); - } - } + // setup extracted mesh data structures given number of points + auto numVertices = dracoMesh->num_points(); - // enumerate the faces and construct the extracted mesh - auto vertexIndices = data.extracted.newIndices.keys(); QHash, int> materialTextureParts; - for (auto i = 0; i < vertexIndices.size(); i += 3) { + data.extracted.mesh.vertices.reserve(numVertices); + data.extracted.mesh.normals.reserve(numVertices); + data.extracted.mesh.texCoords.reserve(numVertices); + data.extracted.mesh.texCoords1.reserve(numVertices); + data.extracted.mesh.colors.reserve(numVertices); + + // enumerate the vertices and construct the extracted mesh + for (int i = 0; i < numVertices; ++i) { + draco::PointIndex vertexIndex(i); + + if (positionAttribute) { + // read position from draco mesh to extracted mesh + auto mappedIndex = positionAttribute->mapped_index(vertexIndex); + + std::array positionValue; + positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); + data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); + } + + if (normalAttribute) { + // read normals from draco mesh to extracted mesh + auto mappedIndex = normalAttribute->mapped_index(vertexIndex); + + std::array normalValue; + normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); + data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + } + + if (texCoordAttribute) { + // read UVs from draco mesh to extracted mesh + auto mappedIndex = texCoordAttribute->mapped_index(vertexIndex); + + std::array texCoordValue; + texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); + data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + } + + if (extraTexCoordAttribute) { + // some meshes have a second set of UVs, read those to extracted mesh + auto mappedIndex = extraTexCoordAttribute->mapped_index(vertexIndex); + + std::array texCoordValue; + extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); + data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + } + + if (colorAttribute) { + // read vertex colors from draco mesh to extracted mesh + auto mappedIndex = colorAttribute->mapped_index(vertexIndex); + + std::array colorValue; + + colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); + data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + } + + int64_t matTexValue[2] = { 0, 0 }; + + if (matTexAttribute) { + // read material ID and texture ID mappings into materials and texture vectors + auto mappedIndex = matTexAttribute->mapped_index(vertexIndex); + + matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); + } + // grab the material ID and texture ID for this face, if we have it - QPair materialTexture(materials.at(i), textures.at(i)); + QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to int& partIndex = materialTextureParts[materialTexture]; @@ -429,12 +428,12 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn partIndex = data.extracted.mesh.parts.size() - 1; } + // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + part.triangleIndices.append(i); - // give the mesh part its indices - part.triangleIndices.append({ vertexIndices[i], vertexIndices[i + 1], vertexIndices[i + 2]}); + data.extracted.newIndices.insert(i, i); } - } } From bb7cd58e9a12dd3a04975b8decfd627a6f987c47 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 16:28:12 -0700 Subject: [PATCH 24/40] fix reference to part index --- libraries/fbx/src/FBXReader_Mesh.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 2d4e722227..30167d9eb5 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -421,15 +421,15 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to - int& partIndex = materialTextureParts[materialTexture]; - if (partIndex == 0) { + int& partIndexPlusOne = materialTextureParts[materialTexture]; + if (partIndexPlusOne == 0) { data.extracted.partMaterialTextures.append(materialTexture); data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); - partIndex = data.extracted.mesh.parts.size() - 1; + partIndexPlusOne = data.extracted.mesh.parts.size(); } // give the mesh part this index - FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(i); data.extracted.newIndices.insert(i, i); From a25e5796dc2d40fbf6850dc51ae125154e7f39bc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 16:35:21 -0700 Subject: [PATCH 25/40] use push_back instead of append for extracted mesh vectors --- libraries/fbx/src/FBXReader_Mesh.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 30167d9eb5..2ff93545ff 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -368,7 +368,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array positionValue; positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); - data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); + data.extracted.mesh.vertices.push_back({ positionValue[0], positionValue[1], positionValue[2] }); } if (normalAttribute) { @@ -377,7 +377,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array normalValue; normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); - data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + data.extracted.mesh.normals.push_back({ normalValue[0], normalValue[1], normalValue[2] }); } if (texCoordAttribute) { @@ -386,7 +386,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array texCoordValue; texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + data.extracted.mesh.texCoords.push_back({ texCoordValue[0], texCoordValue[1] }); } if (extraTexCoordAttribute) { @@ -395,7 +395,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array texCoordValue; extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + data.extracted.mesh.texCoords1.push_back({ texCoordValue[0], texCoordValue[1] }); } if (colorAttribute) { @@ -405,7 +405,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array colorValue; colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); - data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); } int64_t matTexValue[2] = { 0, 0 }; From b253d3b57c8f59a39729aa3e14934368f86ccb8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 17:14:48 -0700 Subject: [PATCH 26/40] fix material triangle index insertion --- libraries/fbx/src/FBXReader_Mesh.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 2ff93545ff..f1524b1a81 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -408,16 +408,22 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); } + data.extracted.newIndices.insert(i, i); + } + + for (int i = 0; i < dracoMesh->num_faces(); ++i) { + // grab the material ID and texture ID for this face, if we have it + auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + int64_t matTexValue[2] = { 0, 0 }; if (matTexAttribute) { // read material ID and texture ID mappings into materials and texture vectors - auto mappedIndex = matTexAttribute->mapped_index(vertexIndex); + auto mappedIndex = matTexAttribute->mapped_index(firstCorner); matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); } - // grab the material ID and texture ID for this face, if we have it QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to @@ -430,9 +436,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; - part.triangleIndices.append(i); - - data.extracted.newIndices.insert(i, i); + part.triangleIndices.append(firstCorner.value()); + part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[1].value()); + part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[2].value()); } } } From c9024f5e8754c0e22bef7ab2c527f7857815d946 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 19:51:49 -0700 Subject: [PATCH 27/40] Update FBXBaker to bake triangleIndices and fix faces being lost --- libraries/baking/src/FBXBaker.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 18e7dbbd76..3407579e9e 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -354,19 +354,15 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto partIndex = 0; + draco::FaceIndex face; for (auto& part : mesh.parts) { - //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); - //Q_ASSERT(part.triangleIndices % 3 == 0); - const auto matTex = extractedMesh.partMaterialTextures[partIndex]; const int64_t matTexData[2] = { matTex.first, matTex.second }; - for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { - auto idx0 = part.quadTrianglesIndices[i]; - auto idx1 = part.quadTrianglesIndices[i + 1]; - auto idx2 = part.quadTrianglesIndices[i + 2]; - - auto face = draco::FaceIndex(i / 3); + auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { + auto idx0 = indices[index]; + auto idx1 = indices[index + 1]; + auto idx2 = indices[index + 2]; if (hasPerFaceMaterials) { meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); @@ -378,7 +374,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (hasNormals) { meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, - &mesh.normals[idx0],&mesh.normals[idx1], + &mesh.normals[idx0], &mesh.normals[idx1], &mesh.normals[idx2]); } if (hasColors) { @@ -396,6 +392,13 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], &mesh.texCoords1[idx2]); } + }; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + addFace(part.quadTrianglesIndices, i, face++); + } + for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { + addFace(part.triangleIndices, i, face++); } partIndex++; From 88b8fb4c5ecce95f298737ff731013e75667870e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 20:02:27 -0700 Subject: [PATCH 28/40] use general worker threads for fbx baking --- libraries/baking/src/FBXBaker.cpp | 1 + tools/oven/src/BakerCLI.cpp | 4 ++-- tools/oven/src/DomainBaker.cpp | 2 +- tools/oven/src/Oven.cpp | 26 +------------------------- tools/oven/src/Oven.h | 2 -- tools/oven/src/ui/ModelBakeWidget.cpp | 2 +- 6 files changed, 6 insertions(+), 31 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 3407579e9e..52c63c26d0 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -397,6 +397,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { addFace(part.quadTrianglesIndices, i, face++); } + for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { addFace(part.triangleIndices, i, face++); } diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index 7b65f8bbf0..5ab995be95 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -42,7 +42,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { // create our appropiate baker if (isFBX) { _baker = std::unique_ptr { new FBXBaker(inputUrl, []() -> QThread* { return qApp->getNextWorkerThread(); }, outputPath) }; - _baker->moveToThread(qApp->getFBXBakerThread()); + _baker->moveToThread(qApp->getNextWorkerThread()); } else if (isSupportedImage) { _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; _baker->moveToThread(qApp->getNextWorkerThread()); @@ -61,4 +61,4 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { void BakerCLI::handleFinishedBaker() { qCDebug(model_baking) << "Finished baking file."; QApplication::exit(_baker.get()->hasErrors()); -} \ No newline at end of file +} diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 5dd7c20b2e..535d9a49a9 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -214,7 +214,7 @@ void DomainBaker::enumerateEntities() { // move the baker to the baker thread // and kickoff the bake - baker->moveToThread(qApp->getFBXBakerThread()); + baker->moveToThread(qApp->getNextWorkerThread()); QMetaObject::invokeMethod(baker.data(), "bake"); // keep track of the total number of baking entities diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index c9d3b4e5f0..d91206a592 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -51,11 +51,7 @@ Oven::Oven(int argc, char* argv[]) : image::setCubeTexturesCompressionEnabled(true); // setup our worker threads - setupWorkerThreads(QThread::idealThreadCount() - 1); - - // Autodesk's SDK means that we need a single thread for all FBX importing/exporting in the same process - // setup the FBX Baker thread - setupFBXBakerThread(); + setupWorkerThreads(QThread::idealThreadCount()); // check if we were passed any command line arguments that would tell us just to run without the GUI if (parser.isSet(CLI_INPUT_PARAMETER) || parser.isSet(CLI_OUTPUT_PARAMETER)) { @@ -81,10 +77,6 @@ Oven::~Oven() { _workerThreads[i]->quit(); _workerThreads[i]->wait(); } - - // cleanup the FBX Baker thread - _fbxBakerThread->quit(); - _fbxBakerThread->wait(); } void Oven::setupWorkerThreads(int numWorkerThreads) { @@ -97,22 +89,6 @@ void Oven::setupWorkerThreads(int numWorkerThreads) { } } -void Oven::setupFBXBakerThread() { - // we're being asked for the FBX baker thread, but we don't have one yet - // so set that up now - _fbxBakerThread = new QThread(this); - _fbxBakerThread->setObjectName("Oven FBX Baker Thread"); -} - -QThread* Oven::getFBXBakerThread() { - if (!_fbxBakerThread->isRunning()) { - // start the FBX baker thread if it isn't running yet - _fbxBakerThread->start(); - } - - return _fbxBakerThread; -} - QThread* Oven::getNextWorkerThread() { // Here we replicate some of the functionality of QThreadPool by giving callers an available worker thread to use. // We can't use QThreadPool because we want to put QObjects with signals/slots on these threads. diff --git a/tools/oven/src/Oven.h b/tools/oven/src/Oven.h index 569b73a3e2..928ba4eb11 100644 --- a/tools/oven/src/Oven.h +++ b/tools/oven/src/Oven.h @@ -34,7 +34,6 @@ public: OvenMainWindow* getMainWindow() const { return _mainWindow; } - QThread* getFBXBakerThread(); QThread* getNextWorkerThread(); private: @@ -42,7 +41,6 @@ private: void setupFBXBakerThread(); OvenMainWindow* _mainWindow; - QThread* _fbxBakerThread; QList _workerThreads; std::atomic _nextWorkerThreadIndex; diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index 926de8bae0..7829993740 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -201,7 +201,7 @@ void ModelBakeWidget::bakeButtonClicked() { }; // move the baker to the FBX baker thread - baker->moveToThread(qApp->getFBXBakerThread()); + baker->moveToThread(qApp->getNextWorkerThread()); // invoke the bake method on the baker thread QMetaObject::invokeMethod(baker.get(), "bake"); From c445d914d7d4704edd7bcbad2716b7144bdbbfce Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 21:59:46 -0700 Subject: [PATCH 29/40] make sure material IDs are de-duplicated --- libraries/baking/src/FBXBaker.cpp | 6 +++--- libraries/fbx/src/FBXReader_Mesh.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 52c63c26d0..271a25e9dd 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -349,7 +349,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (hasPerFaceMaterials) { faceMaterialAttributeID = meshBuilder.AddAttribute( (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, - 2, draco::DT_INT64); + 1, draco::DT_UINT16); } @@ -357,7 +357,6 @@ void FBXBaker::rewriteAndBakeSceneModels() { draco::FaceIndex face; for (auto& part : mesh.parts) { const auto matTex = extractedMesh.partMaterialTextures[partIndex]; - const int64_t matTexData[2] = { matTex.first, matTex.second }; auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { auto idx0 = indices[index]; @@ -365,7 +364,8 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto idx2 = indices[index + 2]; if (hasPerFaceMaterials) { - meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); + uint16_t materialID = matTex.first; + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); } meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index f1524b1a81..323ecac37b 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -415,16 +415,16 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // grab the material ID and texture ID for this face, if we have it auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; - int64_t matTexValue[2] = { 0, 0 }; + uint16_t materialID { 0 }; if (matTexAttribute) { // read material ID and texture ID mappings into materials and texture vectors auto mappedIndex = matTexAttribute->mapped_index(firstCorner); - matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); + matTexAttribute->ConvertValue(mappedIndex, &materialID); } - QPair materialTexture(matTexValue[0], matTexValue[1]); + QPair materialTexture(materialID, 0); // grab or setup the FBXMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; From 087a95a6259c122dc0a8c543c26e0f13a54db3af Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 21:32:37 -0700 Subject: [PATCH 30/40] Add explicit draco encoder options to FBXBaker --- libraries/baking/src/FBXBaker.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 271a25e9dd..1abd411e29 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -423,6 +423,15 @@ void FBXBaker::rewriteAndBakeSceneModels() { } draco::Encoder encoder; + + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, + 14); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, + 12); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, + 10); + encoder.SetSpeedOptions(0, 0); + draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 4efd9b845379f2a1cf19a4eebb45505a40cbfa69 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 23:45:32 -0700 Subject: [PATCH 31/40] set explicit draco encoding options --- libraries/baking/src/FBXBaker.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 1abd411e29..29e5dc6a81 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -424,13 +424,10 @@ void FBXBaker::rewriteAndBakeSceneModels() { draco::Encoder encoder; - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, - 14); - encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, - 12); - encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, - 10); - encoder.SetSpeedOptions(0, 0); + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10); + encoder.SetSpeedOptions(0, 5); draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 8a9e4029ae935e08cc8c6ce50400c51cf7ff3fee Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 00:23:52 -0700 Subject: [PATCH 32/40] use explicit conversion from size_t to int --- libraries/baking/src/FBXBaker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 29e5dc6a81..79a5b9157b 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -434,7 +434,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { FBXNode dracoMeshNode; dracoMeshNode.name = "DracoMesh"; - auto value = QVariant::fromValue(QByteArray(buffer.data(), buffer.size())); + auto value = QVariant::fromValue(QByteArray(buffer.data(), (int) buffer.size())); dracoMeshNode.properties.append(value); @@ -653,13 +653,13 @@ void FBXBaker::handleBakedTexture() { } else { // there was an error baking this texture - add it to our list of errors _errorList.append(bakedTexture->getErrors()); - + // we don't emit finished yet so that the other textures can finish baking first _pendingErrorEmission = true; - + // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list _bakingTextures.remove(bakedTexture->getTextureURL()); - + checkIfTexturesFinished(); } } else { From dc86c6fe73584fd7165194f37c619d1773d593a4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 08:37:36 -0700 Subject: [PATCH 33/40] fixes for warnings in FBXReader/FBXWriter --- libraries/baking/CMakeLists.txt | 2 +- libraries/fbx/CMakeLists.txt | 2 +- libraries/fbx/src/FBXReader.cpp | 6 +++--- libraries/fbx/src/FBXWriter.cpp | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 66cf791776..2304a5e0c0 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 7cead5aa4f..4c81f10302 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu image) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d4a58a1126..e4fea00a34 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -169,7 +169,7 @@ QString getID(const QVariantList& properties, int index = 0) { } /// The names of the joints in the Maya HumanIK rig -static const std::array HUMANIK_JOINTS = { +static const std::array HUMANIK_JOINTS = {{ "RightHand", "RightForeArm", "RightArm", @@ -186,7 +186,7 @@ static const std::array HUMANIK_JOINTS = { "LeftLeg", "RightFoot", "LeftFoot" -}; +}}; class FBXModel { public: @@ -512,7 +512,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector humanIKJointNames; - for (int i = 0; i < HUMANIK_JOINTS.size(); i++) { + for (int i = 0; i < (int) HUMANIK_JOINTS.size(); i++) { QByteArray jointName = HUMANIK_JOINTS[i]; humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); } diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index c14e383402..cc34696f92 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -143,7 +143,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { break; } - // TODO Delete? Do we ever use QList instead of QVector? + // TODO Delete? Do we ever use QList instead of QVector? case QVariant::Type::List: { auto list = prop.toList(); @@ -156,7 +156,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toFloat(); + out << innerProp.toFloat(); } break; @@ -166,7 +166,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toDouble(); + out << innerProp.toDouble(); } break; @@ -176,7 +176,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toLongLong(); + out << innerProp.toLongLong(); } break; @@ -186,7 +186,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toInt(); + out << innerProp.toInt(); } break; @@ -196,7 +196,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toBool(); + out << innerProp.toBool(); } break; } From 4066c9392d309b0532f4e4bb7fe62acbd76a6e77 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:34:15 -0700 Subject: [PATCH 34/40] output from oven into folder with model name --- tools/oven/src/ui/ModelBakeWidget.cpp | 44 +++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index 7829993740..7963b3f3c4 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -156,22 +156,6 @@ void ModelBakeWidget::outputDirectoryChanged(const QString& newDirectory) { } void ModelBakeWidget::bakeButtonClicked() { - // make sure we have a valid output directory - QDir outputDirectory(_outputDirLineEdit->text()); - - outputDirectory.mkdir("."); - if (!outputDirectory.exists()) { - QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); - return; - } - - QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); - QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); - - bakedOutputDirectory.mkdir("."); - originalOutputDirectory.mkdir("."); - - // make sure we have a non empty URL to a model to bake if (_modelLineEdit->text().isEmpty()) { @@ -193,6 +177,34 @@ void ModelBakeWidget::bakeButtonClicked() { qDebug() << "New url: " << modelToBakeURL; } + auto modelName = modelToBakeURL.fileName().left(modelToBakeURL.fileName().lastIndexOf('.')); + + // make sure we have a valid output directory + QDir outputDirectory(_outputDirLineEdit->text()); + QString subFolderName = modelName + "/"; + + // output in a sub-folder with the name of the fbx, potentially suffixed by a number to make it unique + int iteration = 0; + + while (outputDirectory.exists(subFolderName)) { + subFolderName = modelName + "-" + QString::number(++iteration) + "/"; + } + + outputDirectory.mkdir(subFolderName); + + if (!outputDirectory.exists()) { + QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); + return; + } + + outputDirectory.cd(subFolderName); + + QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); + QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); + + bakedOutputDirectory.mkdir("."); + originalOutputDirectory.mkdir("."); + // everything seems to be in place, kick off a bake for this model now auto baker = std::unique_ptr { new FBXBaker(modelToBakeURL, []() -> QThread* { From 1e6154fbc8a500278da067bd18594aee0d291c56 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:45:26 -0700 Subject: [PATCH 35/40] make draco compile flags OS X only --- cmake/externals/draco/CMakeLists.txt | 4 +++- libraries/baking/CMakeLists.txt | 2 +- libraries/fbx/CMakeLists.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 07ce861820..3534461443 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -4,7 +4,9 @@ if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () -set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) +if (APPLE) + set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) +endif () include(ExternalProject) ExternalProject_Add( diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 2304a5e0c0..66cf791776 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 4c81f10302..7cead5aa4f 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu image) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) From c365e1718ee3d999a146db41d2860d1f70a2f3a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:51:26 -0700 Subject: [PATCH 36/40] set include dir for draco the same across the board --- cmake/externals/draco/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 3534461443..44ddd6d3de 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -26,11 +26,7 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (UNIX AND NOT APPLE) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") -else () - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") -endif () +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") if (UNIX) set(LIB_PREFIX "lib") From bfb4dd0cdb00b23133fa7be12f4cba16ea32a75d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 14:00:41 -0700 Subject: [PATCH 37/40] disable warning c4267 when including draco --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ libraries/fbx/src/FBXReader_Mesh.cpp | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 79a5b9157b..5abb3feb0d 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -33,9 +33,19 @@ #include "FBXBaker.h" +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4267 ) +#endif + #include #include +#ifdef _WIN32 +#pragma warning( pop ) +#endif + + FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 323ecac37b..fd9756b4c0 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -9,8 +9,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4267 ) +#endif + #include +#ifdef _WIN32 +#pragma warning( pop ) +#endif + #include #include #include From 9cb4e2c5f2ba61b409f5ae7efa2e32dd8daab4b5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 13:54:04 -0700 Subject: [PATCH 38/40] address code review comments --- libraries/baking/src/FBXBaker.cpp | 14 ++-------- libraries/fbx/src/FBXReader_Mesh.cpp | 38 ++++++++++++---------------- libraries/fbx/src/FBXWriter.cpp | 6 ++--- 3 files changed, 20 insertions(+), 38 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 5abb3feb0d..791d89e503 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -299,7 +299,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { // TODO Pull this out of _geometry instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); - auto mesh = extractedMesh.mesh; + auto& mesh = extractedMesh.mesh; Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); @@ -366,7 +366,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto partIndex = 0; draco::FaceIndex face; for (auto& part : mesh.parts) { - const auto matTex = extractedMesh.partMaterialTextures[partIndex]; + const auto& matTex = extractedMesh.partMaterialTextures[partIndex]; auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { auto idx0 = indices[index]; @@ -447,16 +447,6 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto value = QVariant::fromValue(QByteArray(buffer.data(), (int) buffer.size())); dracoMeshNode.properties.append(value); - - QFile file("C:/Users/huffm/encodedFBX/" + this->_fbxURL.fileName() + "-" + QString::number(meshIndex) + ".drc"); - if (file.open(QIODevice::WriteOnly)) { - file.write(buffer.data(), buffer.size()); - file.close(); - } else { - qWarning() << "Failed to write to: " << file.fileName(); - - } - objectChild.children.push_back(dracoMeshNode); static const std::vector nodeNamesToDelete { diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index fd9756b4c0..bb35447adb 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -361,11 +361,11 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QHash, int> materialTextureParts; - data.extracted.mesh.vertices.reserve(numVertices); - data.extracted.mesh.normals.reserve(numVertices); - data.extracted.mesh.texCoords.reserve(numVertices); - data.extracted.mesh.texCoords1.reserve(numVertices); - data.extracted.mesh.colors.reserve(numVertices); + data.extracted.mesh.vertices.resize(numVertices); + data.extracted.mesh.normals.resize(numVertices); + data.extracted.mesh.texCoords.resize(numVertices); + data.extracted.mesh.texCoords1.resize(numVertices); + data.extracted.mesh.colors.resize(numVertices); // enumerate the vertices and construct the extracted mesh for (int i = 0; i < numVertices; ++i) { @@ -375,46 +375,40 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // read position from draco mesh to extracted mesh auto mappedIndex = positionAttribute->mapped_index(vertexIndex); - std::array positionValue; - positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); - data.extracted.mesh.vertices.push_back({ positionValue[0], positionValue[1], positionValue[2] }); + positionAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.vertices[i])); } if (normalAttribute) { // read normals from draco mesh to extracted mesh auto mappedIndex = normalAttribute->mapped_index(vertexIndex); - std::array normalValue; - normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); - data.extracted.mesh.normals.push_back({ normalValue[0], normalValue[1], normalValue[2] }); + normalAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.normals[i])); } if (texCoordAttribute) { // read UVs from draco mesh to extracted mesh auto mappedIndex = texCoordAttribute->mapped_index(vertexIndex); - std::array texCoordValue; - texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords.push_back({ texCoordValue[0], texCoordValue[1] }); + texCoordAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.texCoords[i])); } if (extraTexCoordAttribute) { // some meshes have a second set of UVs, read those to extracted mesh auto mappedIndex = extraTexCoordAttribute->mapped_index(vertexIndex); - std::array texCoordValue; - extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords1.push_back({ texCoordValue[0], texCoordValue[1] }); + extraTexCoordAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.texCoords1[i])); } if (colorAttribute) { // read vertex colors from draco mesh to extracted mesh auto mappedIndex = colorAttribute->mapped_index(vertexIndex); - std::array colorValue; - - colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); - data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); + colorAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.colors[i])); } data.extracted.newIndices.insert(i, i); @@ -422,7 +416,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn for (int i = 0; i < dracoMesh->num_faces(); ++i) { // grab the material ID and texture ID for this face, if we have it - auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + auto& firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; uint16_t materialID { 0 }; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index cc34696f92..7195d2abb4 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -19,9 +19,8 @@ void writeVector(QDataStream& out, char ch, QVector list) { out << (int32_t)list.length(); out << (int32_t)0; out << (int32_t)0; - for (auto& value : list) { - out << value; - } + + out.writeBytes(reinterpret_cast(list.constData()), list.length() * sizeof(T)); } @@ -98,7 +97,6 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QVariant::Type::Bool: out.device()->write("C", 1); - //out.device()->write(prop.toBool() ? 1 : 0, 1); out << prop.toBool(); break; From bb98df38d4c9f4add6b320e2bbbaaa1295b0b96b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 14:42:40 -0700 Subject: [PATCH 39/40] remove re-grabbing of draco faces --- libraries/fbx/src/FBXReader_Mesh.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index bb35447adb..163a92e3bd 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -416,7 +416,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn for (int i = 0; i < dracoMesh->num_faces(); ++i) { // grab the material ID and texture ID for this face, if we have it - auto& firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + auto& dracoFace = dracoMesh->face(draco::FaceIndex(i)); + auto& firstCorner = dracoFace[0]; uint16_t materialID { 0 }; @@ -440,8 +441,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(firstCorner.value()); - part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[1].value()); - part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[2].value()); + part.triangleIndices.append(dracoFace[1].value()); + part.triangleIndices.append(dracoFace[2].value()); } } } From 43196cd0a66cb048d7198a225ebe396ed61b3bc4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 14:46:48 -0700 Subject: [PATCH 40/40] fix buffer resizing to only occur when attribute is present --- libraries/fbx/src/FBXReader_Mesh.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 163a92e3bd..bef36770a0 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -362,10 +362,22 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QHash, int> materialTextureParts; data.extracted.mesh.vertices.resize(numVertices); - data.extracted.mesh.normals.resize(numVertices); - data.extracted.mesh.texCoords.resize(numVertices); - data.extracted.mesh.texCoords1.resize(numVertices); - data.extracted.mesh.colors.resize(numVertices); + + if (normalAttribute) { + data.extracted.mesh.normals.resize(numVertices); + } + + if (texCoordAttribute) { + data.extracted.mesh.texCoords.resize(numVertices); + } + + if (extraTexCoordAttribute) { + data.extracted.mesh.texCoords1.resize(numVertices); + } + + if (colorAttribute) { + data.extracted.mesh.colors.resize(numVertices); + } // enumerate the vertices and construct the extracted mesh for (int i = 0; i < numVertices; ++i) {