From 471baf83c69eab1589ec2085b1a43dc52612f88f Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Tue, 25 Sep 2018 07:56:44 +0100
Subject: [PATCH 01/10] Support vertex colours and multiple unnamed mesh
 materials.

# Conflicts:
#	libraries/fbx/src/GLTFSerializer.cpp
---
 libraries/fbx/src/GLTFSerializer.cpp | 167 ++++++++++++++++++---------
 libraries/fbx/src/GLTFSerializer.h   |  55 ++++++++-
 2 files changed, 166 insertions(+), 56 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 622fb92ce7..ff2ab8a6d4 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -261,6 +261,41 @@ bool GLTFSerializer::setAsset(const QJsonObject& object) {
     return isAssetDefined;
 }
 
+GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices(const QJsonObject& object) {
+    GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices accessorSparseIndices;
+
+    getIntVal(object, "bufferView", accessorSparseIndices.bufferView, accessorSparseIndices.defined);
+    getIntVal(object, "byteOffset", accessorSparseIndices.byteOffset, accessorSparseIndices.defined);
+    getIntVal(object, "componentType", accessorSparseIndices.componentType, accessorSparseIndices.defined);
+
+    return accessorSparseIndices;
+}
+
+GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues(const QJsonObject& object) {
+    GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues accessorSparseValues;
+
+    getIntVal(object, "bufferView", accessorSparseValues.bufferView, accessorSparseValues.defined);
+    getIntVal(object, "byteOffset", accessorSparseValues.byteOffset, accessorSparseValues.defined);
+
+    return accessorSparseValues;
+}
+
+GLTFAccessor::GLTFAccessorSparse GLTFSerializer::createAccessorSparse(const QJsonObject& object) {
+    GLTFAccessor::GLTFAccessorSparse accessorSparse;
+
+    getIntVal(object, "count", accessorSparse.count, accessorSparse.defined);
+    QJsonObject sparseIndicesObject;
+    if (getObjectVal(object, "indices", sparseIndicesObject, accessorSparse.defined)) {
+        accessorSparse.indices = createAccessorSparseIndices(sparseIndicesObject);
+    }
+    QJsonObject sparseValuesObject;
+    if (getObjectVal(object, "values", sparseValuesObject, accessorSparse.defined)) {
+        accessorSparse.values = createAccessorSparseValues(sparseValuesObject);
+    }
+
+    return accessorSparse;
+}
+
 bool GLTFSerializer::addAccessor(const QJsonObject& object) {
     GLTFAccessor accessor;
     
@@ -273,6 +308,12 @@ bool GLTFSerializer::addAccessor(const QJsonObject& object) {
     if (getStringVal(object, "type", type, accessor.defined)) {
         accessor.type = getAccessorType(type);
     }
+
+    QJsonObject sparseObject;
+    if (getObjectVal(object, "sparse", sparseObject, accessor.defined)) {
+        accessor.sparse = createAccessorSparse(sparseObject);
+    }
+
     getDoubleArrayVal(object, "max", accessor.max, accessor.defined);
     getDoubleArrayVal(object, "min", accessor.min, accessor.defined);
 
@@ -783,7 +824,7 @@ void GLTFSerializer::generateTargetData(int index, float weight, QVector<glm::ve
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) {
     int numNodes = _file.nodes.size();
 
-    // Build dependencies
+    //Build dependencies
     QVector<int> parents;
     QVector<int> sortedNodes;
     parents.fill(-1, numNodes);
@@ -802,18 +843,18 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
     nodecount = 0;
     foreach(auto &node, _file.nodes) {
         // collect node transform
-        _file.nodes[nodecount].transforms.push_back(getModelTransform(node));
+        _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); 
         int parentIndex = parents[nodecount];
         while (parentIndex != -1) {
             const auto& parentNode = _file.nodes[parentIndex];
             // collect transforms for a node's parents, grandparents, etc.
             _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode));
             parentIndex = parents[parentIndex];
-        }
+            }
         nodecount++;
     }
 
- 
+
     // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order
     // therefore we need to re-initialize the order in which nodes will be parsed
     QVector<bool> hasBeenSorted;
@@ -837,10 +878,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     sortedNodes[j] = currentNode;
                     i++;
                     currentNode = sortedNodes[i];
-                }
+    }
                 j++;
-            }
-        }
+    }
+    }
     }
 
 
@@ -868,7 +909,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
         joint.translation = extractTranslation(joint.transform);
         joint.rotation = glmExtractRotation(joint.transform);
         glm::vec3 scale = extractScale(joint.transform);
-        joint.postTransform = glm::scale(glm::mat4(), scale);  
+        joint.postTransform = glm::scale(glm::mat4(), scale);        
 
         joint.name = node.name;
         joint.isSkeletonJoint = false;
@@ -914,12 +955,18 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
     }
 
 
-    // Build materials
+    //Build materials
     QVector<QString> materialIDs;
     QString unknown = "Default";
     int ukcount = 0;
     foreach(auto material, _file.materials) {
-        QString mid = (material.defined["name"]) ? material.name : unknown + ukcount++;
+        if (!(material.defined["name"])) {
+            QString name = unknown + QString::number(ukcount++);
+            material.name = name;
+            material.defined.insert("name", true);
+        }
+
+        QString mid = material.name;
         materialIDs.push_back(mid);
     }
 
@@ -928,6 +975,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
         hfmModel.materials[matid] = HFMMaterial();
         HFMMaterial& hfmMaterial = hfmModel.materials[matid];
         hfmMaterial._material = std::make_shared<graphics::Material>();
+		hfmMaterial.name = hfmMaterial.materialID = matid;
         setHFMMaterial(hfmMaterial, _file.materials[i]);
     }
 
@@ -938,30 +986,31 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
         auto& node = _file.nodes[nodeIndex];
 
         if (node.defined["mesh"]) {
-            foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
-                hfmModel.meshes.append(HFMMesh());
-                HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
-                if (!hfmModel.hasSkeletonJoints) { 
-                    HFMCluster cluster;
-                    cluster.jointIndex = nodecount;
-                    cluster.inverseBindMatrix = glm::mat4();
-                    cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
-                    mesh.clusters.append(cluster);
+
+            hfmModel.meshes.append(HFMMesh());
+            HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
+            if (!hfmModel.hasSkeletonJoints) { 
+                HFMCluster cluster;
+                cluster.jointIndex = nodecount;
+                cluster.inverseBindMatrix = glm::mat4();
+                cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
+                mesh.clusters.append(cluster);
                 } else { // skinned model
                     for (int j = 0; j < numNodes; j++) {
-                        HFMCluster cluster;
-                        cluster.jointIndex = j;
-                        cluster.inverseBindMatrix = jointInverseBindTransforms[j];
-                        cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
-                        mesh.clusters.append(cluster);
-                    }
+                    HFMCluster cluster;
+                    cluster.jointIndex = j;
+                    cluster.inverseBindMatrix = jointInverseBindTransforms[j];
+                    cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
+                    mesh.clusters.append(cluster);
                 }
-                HFMCluster root; 
-                root.jointIndex = 0;
-                root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
-                root.inverseBindTransform = Transform(root.inverseBindMatrix);
-                mesh.clusters.append(root);
+            }
+            HFMCluster root; 
+            root.jointIndex = 0;
+            root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
+            root.inverseBindTransform = Transform(root.inverseBindMatrix);
+            mesh.clusters.append(root);
 
+            foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
                 HFMMeshPart part = HFMMeshPart();
 
                 int indicesAccessorIdx = primitive.indices;
@@ -972,10 +1021,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                 int indicesAccBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
 
-                QVector<int> raw_indices;
-                QVector<glm::vec3> raw_vertices;
-                QVector<glm::vec3> raw_normals;
-
                 bool success = addArrayOfType(indicesBuffer.blob, 
                     indicesBufferview.byteOffset + indicesAccBoffset, 
                     indicesAccessor.count, 
@@ -988,6 +1033,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     continue;
                 }
 
+                // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh
+                int prevMeshVerticesCount = mesh.vertices.count();
+                if (prevMeshVerticesCount > 0) {
+                    for (int i = 0; i < part.triangleIndices.count(); i++) {
+                        part.triangleIndices[i] += prevMeshVerticesCount;
+                    }
+                }
+
                 QList<QString> keys = primitive.attributes.values.keys();
                 QVector<uint16_t> clusterJoints;
                 QVector<float> clusterWeights;
@@ -1029,22 +1082,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         for (int n = 0; n < normals.size(); n = n + 3) {
                             mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
                         }
-                    } else if (key == "COLOR_0") {
-                        QVector<float> colors;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            colors,
-                            accessor.type, 
-                            accessor.componentType);
-                        if (!success) {
-                            qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
-                            continue;
-                        }
-                        int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
-                        for (int n = 0; n < colors.size() - 3; n += stride) {
-                            mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
-                        }
                     } else if (key == "TANGENT") {
                         QVector<float> tangents;
                         success = addArrayOfType(buffer.blob, 
@@ -1093,7 +1130,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         for (int n = 0; n < texcoords.size(); n = n + 2) {
                             mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
                         }
-                    } else if (key == "JOINTS_0") {
+                    } else if (key == "COLOR_0") {
+                        QVector<float> colors;
+                        success = addArrayOfType(buffer.blob,
+                            bufferview.byteOffset + accBoffset,
+                            accessor.count,
+                            colors,
+                            accessor.type,
+                            accessor.componentType);
+                        if (!success) {
+                            qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
+                            continue;
+                        }
+                        int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
+                        for (int n = 0; n < colors.size() - 3; n += stride) {
+                            mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
+                        }
+					} else if (key == "JOINTS_0") {
                         QVector<uint16_t> joints;
                         success = addArrayOfType(buffer.blob,
                             bufferview.byteOffset + accBoffset,
@@ -1251,6 +1304,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                
                 mesh.meshIndex = hfmModel.meshes.size();
             }
+
+            mesh.meshExtents.reset();
+            foreach(const glm::vec3& vertex, mesh.vertices) {
+                mesh.meshExtents.addPoint(vertex);
+                hfmModel.meshExtents.addPoint(vertex);
+            }
+           
+            mesh.meshIndex = hfmModel.meshes.size();
         }
         nodecount++;
     }
@@ -1404,10 +1465,6 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) {
 void GLTFSerializer::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) {
 
 
-    if (material.defined["name"]) {
-        fbxmat.name = fbxmat.materialID = material.name;
-    }
-    
     if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) {
         glm::vec3 emissive = glm::vec3(material.emissiveFactor[0], 
                                        material.emissiveFactor[1], 
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index b1c1bc4e44..fa5a640fca 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -481,6 +481,49 @@ namespace GLTFAccessorComponentType {
     };
 }
 struct GLTFAccessor {
+    struct GLTFAccessorSparse {
+        struct GLTFAccessorSparseIndices {
+            int bufferView;
+            int byteOffset{ 0 };
+            int componentType;
+
+            QMap<QString, bool> defined;
+            void dump() {
+                if (defined["bufferView"]) {
+                    qCDebug(modelformat) << "bufferView: " << bufferView;
+                }
+                if (defined["byteOffset"]) {
+                    qCDebug(modelformat) << "byteOffset: " << byteOffset;
+                }
+                if (defined["componentType"]) {
+                    qCDebug(modelformat) << "componentType: " << componentType;
+                }
+            }
+        };
+        struct GLTFAccessorSparseValues {
+            int bufferView;
+            int byteOffset{ 0 };
+
+            QMap<QString, bool> defined;
+            void dump() {
+                if (defined["bufferView"]) {
+                    qCDebug(modelformat) << "bufferView: " << bufferView;
+                }
+                if (defined["byteOffset"]) {
+                    qCDebug(modelformat) << "byteOffset: " << byteOffset;
+                }
+            }
+        };
+
+        int count;
+        GLTFAccessorSparseIndices indices;
+        GLTFAccessorSparseValues values;
+
+        QMap<QString, bool> defined;
+        void dump() {
+        
+        }
+    };
     int bufferView;
     int byteOffset { 0 };
     int componentType; //required
@@ -489,6 +532,7 @@ struct GLTFAccessor {
     bool normalized{ false };
     QVector<double> max;
     QVector<double> min;
+    GLTFAccessorSparse sparse;
     QMap<QString, bool> defined;
     void dump() {
         if (defined["bufferView"]) {
@@ -521,6 +565,10 @@ struct GLTFAccessor {
                 qCDebug(modelformat) << m;
             }
         }
+        if (defined["sparse"]) {
+            qCDebug(modelformat) << "sparse: ";
+            sparse.dump();
+        }
     }
 };
 
@@ -763,6 +811,11 @@ private:
                             int& outidx, QMap<QString, bool>& defined);
 
     bool setAsset(const QJsonObject& object);
+
+    GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices createAccessorSparseIndices(const QJsonObject& object);
+    GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues createAccessorSparseValues(const QJsonObject& object);
+    GLTFAccessor::GLTFAccessorSparse createAccessorSparse(const QJsonObject& object);
+
     bool addAccessor(const QJsonObject& object);
     bool addAnimation(const QJsonObject& object);
     bool addBufferView(const QJsonObject& object);
@@ -782,7 +835,7 @@ private:
     template<typename T, typename L>
     bool readArray(const hifi::ByteArray& bin, int byteOffset, int count,
                    QVector<L>& outarray, int accessorType);
-    
+
     template<typename T>
     bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count,
                         QVector<T>& outarray, int accessorType, int componentType);

From a4b3f02139b09d319833616a85acf860255e5047 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Wed, 7 Nov 2018 20:43:42 +0000
Subject: [PATCH 02/10] Added support for automatic normals generation, sparse
 accessors, extra threshold for thin bounding boxes, and general refactoring.

# Conflicts:
#	libraries/fbx/src/GLTFSerializer.cpp
---
 libraries/fbx/src/GLTFSerializer.cpp | 440 +++++++++++++++++++++------
 libraries/fbx/src/GLTFSerializer.h   |   3 +
 2 files changed, 348 insertions(+), 95 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index ff2ab8a6d4..78569fa78f 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -37,6 +37,28 @@
 
 #include "FBXSerializer.h"
 
+#define GLTF_GET_INDICIES(acc_count) int index1 = (indices[n + 0] * acc_count); int index2 = (indices[n + 1] * acc_count); int index3 = (indices[n + 2] * acc_count);
+
+#define GLTF_APPEND_ARRAY_1(new_array, old_array) GLTF_GET_INDICIES(1) \
+new_array.append(old_array[index1]); \
+new_array.append(old_array[index2]); \
+new_array.append(old_array[index3]);
+
+#define GLTF_APPEND_ARRAY_2(new_array, old_array) GLTF_GET_INDICIES(2) \
+new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); \
+new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); \
+new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]);
+
+#define GLTF_APPEND_ARRAY_3(new_array, old_array) GLTF_GET_INDICIES(3) \
+new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); new_array.append(old_array[index1 + 2]); \
+new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); new_array.append(old_array[index2 + 2]); \
+new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]); new_array.append(old_array[index3 + 2]);
+
+#define GLTF_APPEND_ARRAY_4(new_array, old_array) GLTF_GET_INDICIES(4) \
+new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); new_array.append(old_array[index1 + 2]); new_array.append(old_array[index1 + 3]); \
+new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); new_array.append(old_array[index2 + 2]); new_array.append(old_array[index2 + 3]); \
+new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]); new_array.append(old_array[index3 + 2]); new_array.append(old_array[index3 + 3]);
+
 bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname,
                               QString& value, QMap<QString, bool>&  defined) {
     bool _defined = (object.contains(fieldname) && object[fieldname].isString());
@@ -1016,17 +1038,23 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 int indicesAccessorIdx = primitive.indices;
 
                 GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx];
-                GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
-                GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer];
 
-                int indicesAccBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
+                // Buffers
+                QVector<int> indices;
+                QVector<float> vertices;
+                QVector<float> normals;
+                QVector<float> tangents;
+                int tangent_stride = 0;
+                QVector<float> texcoords;
+                QVector<float> texcoords2;
+                QVector<float> colors;
+                int color_stride = 0;
+                QVector<uint16_t> joints;
+                int joint_stride = 0;
+                QVector<float> weights;
+                int weight_stride = 0;
 
-                bool success = addArrayOfType(indicesBuffer.blob, 
-                    indicesBufferview.byteOffset + indicesAccBoffset, 
-                    indicesAccessor.count, 
-                    part.triangleIndices, 
-                    indicesAccessor.type, 
-                    indicesAccessor.componentType);
+                bool success = addArrayOfFromAccessor(indicesAccessor, indices);
 
                 if (!success) {
                     qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url;
@@ -1035,11 +1063,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                 // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh
                 int prevMeshVerticesCount = mesh.vertices.count();
-                if (prevMeshVerticesCount > 0) {
-                    for (int i = 0; i < part.triangleIndices.count(); i++) {
-                        part.triangleIndices[i] += prevMeshVerticesCount;
-                    }
-                }
 
                 QList<QString> keys = primitive.attributes.values.keys();
                 QVector<uint16_t> clusterJoints;
@@ -1049,136 +1072,290 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     int accessorIdx = primitive.attributes.values[key];
 
                     GLTFAccessor& accessor = _file.accessors[accessorIdx];
-                    GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView];
-                    GLTFBuffer& buffer = _file.buffers[bufferview.buffer];
 
                     int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
                     if (key == "POSITION") {
-                        QVector<float> vertices;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, vertices, 
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, vertices);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < vertices.size(); n = n + 3) {
-                            mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2]));
+
+                        if (accessor.type != GLTFAccessorType::VEC3) {
+                            qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url;
+                            continue;
                         }
                     } else if (key == "NORMAL") {
-                        QVector<float> normals;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            normals, 
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, normals);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < normals.size(); n = n + 3) {
-                            mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
+
+                        if (accessor.type != GLTFAccessorType::VEC3) {
+                            qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url;
+                            continue;
                         }
                     } else if (key == "TANGENT") {
-                        QVector<float> tangents;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            tangents,
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, tangents);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
                             continue;
                         }
-                        // tangents can be a vec3 or a vec4 which includes a w component (of -1 or 1)
-                        int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
-                        for (int n = 0; n < tangents.size() - 3; n += stride) {
-                            float tanW = stride == 4 ? tangents[n + 3] : 1; 
-                            mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
+
+                        if (accessor.type == GLTFAccessorType::VEC4) {
+                            tangent_stride = 4;
+                        } else if (accessor.type == GLTFAccessorType::VEC3) {
+                            tangent_stride = 3;
+                        } else {
+                            qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url;
+                            continue;
                         }
                     } else if (key == "TEXCOORD_0") {
-                        QVector<float> texcoords;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            texcoords, 
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, texcoords);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < texcoords.size(); n = n + 2) {
-                            mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
+
+                        if (accessor.type != GLTFAccessorType::VEC2) {
+                            qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url;
+                            continue;
                         }
                     } else if (key == "TEXCOORD_1") {
-                        QVector<float> texcoords;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            texcoords, 
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, texcoords2);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < texcoords.size(); n = n + 2) {
-                            mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
+
+                        if (accessor.type != GLTFAccessorType::VEC2) {
+                            qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url;
+                            continue;
                         }
                     } else if (key == "COLOR_0") {
-                        QVector<float> colors;
-                        success = addArrayOfType(buffer.blob,
-                            bufferview.byteOffset + accBoffset,
-                            accessor.count,
-                            colors,
-                            accessor.type,
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, colors);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
                             continue;
                         }
-                        int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
-                        for (int n = 0; n < colors.size() - 3; n += stride) {
-                            mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
+
+                        if (accessor.type == GLTFAccessorType::VEC4) {
+                            color_stride = 4;
+                        } else if (accessor.type == GLTFAccessorType::VEC3) {
+                            color_stride = 3;
+                        } else {
+                            qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url;
+                            continue;
                         }
-					} else if (key == "JOINTS_0") {
-                        QVector<uint16_t> joints;
-                        success = addArrayOfType(buffer.blob,
-                            bufferview.byteOffset + accBoffset,
-                            accessor.count,
-                            joints,
-                            accessor.type, 
-                            accessor.componentType);
+                    } else if (key == "JOINTS_0") {
+                        success = addArrayOfFromAccessor(accessor, joints);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < joints.size(); n++) {
-                            clusterJoints.push_back(joints[n]);
+
+                        if (accessor.type == GLTFAccessorType::VEC4) {
+                            joint_stride = 4;
+                        } else if (accessor.type == GLTFAccessorType::VEC3) {
+                            joint_stride = 3;
+                        } else if (accessor.type == GLTFAccessorType::VEC2) {
+                            joint_stride = 2;
+                        } else if (accessor.type == GLTFAccessorType::SCALAR) {
+                            joint_stride = 1;
+                        } else {
+                            qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url;
+                            continue;
                         }
                     } else if (key == "WEIGHTS_0") {
-                        QVector<float> weights;
-                        success = addArrayOfType(buffer.blob, 
-                            bufferview.byteOffset + accBoffset, 
-                            accessor.count, 
-                            weights,
-                            accessor.type, 
-                            accessor.componentType);
+                        success = addArrayOfFromAccessor(accessor, weights);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
                             continue;
                         }
-                        for (int n = 0; n < weights.size(); n++) {
-                            clusterWeights.push_back(weights[n]);
+
+                        if (accessor.type == GLTFAccessorType::VEC4) {
+                            weight_stride = 4;
+                        } else if (accessor.type == GLTFAccessorType::VEC3) {
+                            weight_stride = 3;
+                        } else if (accessor.type == GLTFAccessorType::VEC2) {
+                            weight_stride = 2;
+                        } else if (accessor.type == GLTFAccessorType::SCALAR) {
+                            weight_stride = 1;
+                        } else {
+                            qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url;
+                            continue;
                         }
                     }
                 }
-            
+
+                // generate the normals if they don't exist
+                if (normals.size() == 0) {
+                    QVector<int> new_indices;
+                    QVector<float> new_vertices;
+                    QVector<float> new_normals;
+                    QVector<float> new_tangents;
+                    QVector<float> new_texcoords;
+                    QVector<float> new_texcoords2;
+                    QVector<float> new_colors;
+                    QVector<uint16_t> new_joints;
+                    QVector<float> new_weights;
+
+                    for (int n = 0; n < indices.size(); n = n + 3) {
+                        int v1_index = (indices[n + 0] * 3);
+                        int v2_index = (indices[n + 1] * 3);
+                        int v3_index = (indices[n + 2] * 3);
+
+                        glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]);
+                        glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]);
+                        glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]);
+
+                        new_vertices.append(v1.x); new_vertices.append(v1.y); new_vertices.append(v1.z);
+                        new_vertices.append(v2.x); new_vertices.append(v2.y); new_vertices.append(v2.z);
+                        new_vertices.append(v3.x); new_vertices.append(v3.y); new_vertices.append(v3.z);
+
+                        glm::vec3 norm = glm::normalize(glm::cross(v2 - v1, v3 - v1));
+
+                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
+                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
+                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
+
+                        if (tangents.size() > 0) {
+							if (tangent_stride == 4) {
+                                GLTF_APPEND_ARRAY_4(new_tangents, tangents)
+                            } else {
+                                GLTF_APPEND_ARRAY_3(new_tangents, tangents)
+                            }
+                        }
+
+                        if (texcoords.size() > 0) {
+                            GLTF_APPEND_ARRAY_2(new_texcoords, texcoords)
+                        }
+
+                        if (texcoords2.size() > 0) {
+                            GLTF_APPEND_ARRAY_2(new_texcoords2, texcoords2)
+                        }
+
+                        if (colors.size() > 0) {
+                            if (color_stride == 4) {
+                                GLTF_APPEND_ARRAY_4(new_colors, colors)
+                            } else {
+                                GLTF_APPEND_ARRAY_3(new_colors, colors)
+                            }
+                        }
+
+                        if (joints.size() > 0) {
+                            if (joint_stride == 4) {
+                                GLTF_APPEND_ARRAY_4(new_joints, joints)
+                            } else if (joint_stride == 3) {
+                                GLTF_APPEND_ARRAY_3(new_joints, joints)
+                            } else if (joint_stride == 2) {
+                                GLTF_APPEND_ARRAY_2(new_joints, joints)
+                            } else {
+                                GLTF_APPEND_ARRAY_1(new_joints, joints)
+                            }
+                        }
+
+                        if (weights.size() > 0) {
+                            if (weight_stride == 4) {
+                                GLTF_APPEND_ARRAY_4(new_weights, weights)
+                            } else if (weight_stride == 3) {
+                                GLTF_APPEND_ARRAY_3(new_weights, weights)
+                            } else if (weight_stride == 2) {
+                                GLTF_APPEND_ARRAY_2(new_weights, weights)
+                            } else {
+                                GLTF_APPEND_ARRAY_1(new_weights, weights)
+                            }
+                        }
+
+                        new_indices.append(n);
+                        new_indices.append(n + 1);
+                        new_indices.append(n + 2);
+                    }
+
+                    vertices = new_vertices;
+                    normals = new_normals;
+                    tangents = new_tangents;
+                    indices = new_indices;
+                    texcoords = new_texcoords;
+                    texcoords2 = new_texcoords2;
+                    colors = new_colors;
+                    joints = new_joints;
+                    weights = new_weights;
+                }
+
+                for (int n = 0; n < indices.count(); n++) {
+                    part.triangleIndices.push_back(indices[n] + prevMeshVerticesCount);
+                }
+
+                for (int n = 0; n < vertices.size(); n = n + 3) {
+                    mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2]));
+                }
+
+                for (int n = 0; n < normals.size(); n = n + 3) {
+                    mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
+                }
+
+                for (int n = 0; n < tangents.size(); n += tangent_stride) {
+                    float tanW = tangent_stride == 4 ? tangents[n + 3] : 1;
+                    mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
+                }
+
+                for (int n = 0; n < texcoords.size(); n = n + 2) {
+                    mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
+                }
+                for (int n = 0; n < texcoords2.size(); n = n + 2) {
+                    mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1]));
+                }
+
+                for (int n = 0; n < colors.size(); n += color_stride) {
+                    mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
+                }
+
+                for (int n = 0; n < joints.size(); n += joint_stride) {
+                    clusterJoints.push_back(joints[n]);
+                    if (joint_stride > 1) {
+                        clusterJoints.push_back(joints[n + 1]);
+                        if (joint_stride > 2) {
+                            clusterJoints.push_back(joints[n + 2]);
+                            if (joint_stride > 3) {
+                                clusterJoints.push_back(joints[n + 3]);
+                            } else {
+                                clusterJoints.push_back(0);
+                            }
+                        } else {
+                            clusterJoints.push_back(0);
+                            clusterJoints.push_back(0);
+                        }
+                    } else {
+                        clusterJoints.push_back(0);
+                        clusterJoints.push_back(0);
+                        clusterJoints.push_back(0);
+                    }
+                }
+
+                for (int n = 0; n < weights.size(); n += weight_stride) {
+                    clusterWeights.push_back(weights[n]);
+                    if (weight_stride > 1) {
+                        clusterWeights.push_back(weights[n + 1]);
+                        if (weight_stride > 2) {
+                            clusterWeights.push_back(weights[n + 2]);
+                            if (weight_stride > 3) {
+                                clusterWeights.push_back(weights[n + 3]);
+                            } else {
+                                clusterWeights.push_back(0);
+                            }
+                        } else {
+                            clusterWeights.push_back(0);
+                            clusterWeights.push_back(0);
+                        }
+                    } else {
+                        clusterWeights.push_back(0);
+                        clusterWeights.push_back(0);
+                        clusterWeights.push_back(0);
+                    }
+                }
+
                 // Build weights (adapted from FBXSerializer.cpp)
                 if (hfmModel.hasSkeletonJoints) {
                     int numClusterIndices = clusterJoints.size();
@@ -1310,6 +1487,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 mesh.meshExtents.addPoint(vertex);
                 hfmModel.meshExtents.addPoint(vertex);
             }
+
+            // Add epsilon to mesh extents to compensate for planar meshes
+            mesh.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON);
+            mesh.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON);
+            hfmModel.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON);
+            hfmModel.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON);
            
             mesh.meshIndex = hfmModel.meshes.size();
         }
@@ -1601,7 +1784,74 @@ bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset,
     return false;
 }
 
-void GLTFSerializer::retriangulate(const QVector<int>& inIndices, const QVector<glm::vec3>& in_vertices,
+template <typename T>
+bool GLTFSerializer::addArrayOfFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray) {
+    bool success = true;
+
+    if (accessor.defined["bufferView"]) {
+        GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView];
+        GLTFBuffer& buffer = _file.buffers[bufferview.buffer];
+
+        int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
+
+        success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, accessor.count, outarray, accessor.type,
+                                 accessor.componentType);
+    } else {
+        for (int i = 0; i < accessor.count; i++) {
+            T value;
+            memset(&value, 0, sizeof(T));  // Make sure the dummy array is initalised to zero.
+            outarray.push_back(value);
+        }
+    }
+
+    if (success) {
+        if (accessor.defined["sparse"]) {
+            QVector<int> out_sparse_indices_array;
+
+            GLTFBufferView& sparseIndicesBufferview = _file.bufferviews[accessor.sparse.indices.bufferView];
+            GLTFBuffer& sparseIndicesBuffer = _file.buffers[sparseIndicesBufferview.buffer];
+
+            int accSIBoffset = accessor.sparse.indices.defined["byteOffset"] ? accessor.sparse.indices.byteOffset : 0;
+
+            success = addArrayOfType(sparseIndicesBuffer.blob, sparseIndicesBufferview.byteOffset + accSIBoffset,
+                                     accessor.sparse.count, out_sparse_indices_array, GLTFAccessorType::SCALAR,
+                                     accessor.sparse.indices.componentType);
+            if (success) {
+                QVector<T> out_sparse_values_array;
+
+                GLTFBufferView& sparseValuesBufferview = _file.bufferviews[accessor.sparse.values.bufferView];
+                GLTFBuffer& sparseValuesBuffer = _file.buffers[sparseValuesBufferview.buffer];
+
+                int accSVBoffset = accessor.sparse.values.defined["byteOffset"] ? accessor.sparse.values.byteOffset : 0;
+
+                success = addArrayOfType(sparseValuesBuffer.blob, sparseValuesBufferview.byteOffset + accSVBoffset,
+                                         accessor.sparse.count, out_sparse_values_array, accessor.type, accessor.componentType);
+
+                if (success) {
+                    for (int i = 0; i < accessor.sparse.count; i++) {
+                        if ((i * 3) + 2 < out_sparse_values_array.size()) { 
+                            if ((out_sparse_indices_array[i] * 3) + 2 < outarray.length()) {
+                                for (int j = 0; j < 3; j++) {
+                                    outarray[(out_sparse_indices_array[i] * 3) + j] = out_sparse_values_array[(i * 3) + j];
+                                }
+                            } else {
+                                success = false;
+                                break;
+                            }
+                        } else {
+                            success = false;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return success;
+}
+
+void GLTFSerializer::retriangulate(const QVector<int>& inIndices, const QVector<glm::vec3>& in_vertices, 
                                const QVector<glm::vec3>& in_normals, QVector<int>& outIndices, 
                                QVector<glm::vec3>& out_vertices, QVector<glm::vec3>& out_normals) {
     for (int i = 0; i < inIndices.size(); i = i + 3) {
@@ -1766,7 +2016,7 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
 
     qCDebug(modelformat) << "---------------- Joints ----------------";
 
-    foreach(HFMJoint joint, hfmModel.joints) {
+    foreach (HFMJoint joint, hfmModel.joints) {
         qCDebug(modelformat) << "\n";
         qCDebug(modelformat) << "    shapeInfo.avgPoint =" << joint.shapeInfo.avgPoint;
         qCDebug(modelformat) << "    shapeInfo.debugLines =" << joint.shapeInfo.debugLines;
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index fa5a640fca..e7dc76e084 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -840,6 +840,9 @@ private:
     bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count,
                         QVector<T>& outarray, int accessorType, int componentType);
 
+    template <typename T>
+    bool addArrayOfFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray);
+
     void retriangulate(const QVector<int>& in_indices, const QVector<glm::vec3>& in_vertices, 
                        const QVector<glm::vec3>& in_normals, QVector<int>& out_indices, 
                        QVector<glm::vec3>& out_vertices, QVector<glm::vec3>& out_normals);

From b00e0640c5169efacdef4e230fc0271deaa0d745 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Thu, 11 Apr 2019 22:09:36 +0100
Subject: [PATCH 03/10] Convert to camel case, removed unused variable, replace
 tabs with spaces, remove deprecated normals generation.

---
 libraries/fbx/src/GLTFSerializer.cpp | 186 +++++++--------------------
 1 file changed, 45 insertions(+), 141 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 78569fa78f..e7b9dd79f7 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -37,27 +37,27 @@
 
 #include "FBXSerializer.h"
 
-#define GLTF_GET_INDICIES(acc_count) int index1 = (indices[n + 0] * acc_count); int index2 = (indices[n + 1] * acc_count); int index3 = (indices[n + 2] * acc_count);
+#define GLTF_GET_INDICIES(accCount) int index1 = (indices[n + 0] * accCount); int index2 = (indices[n + 1] * accCount); int index3 = (indices[n + 2] * accCount);
 
-#define GLTF_APPEND_ARRAY_1(new_array, old_array) GLTF_GET_INDICIES(1) \
-new_array.append(old_array[index1]); \
-new_array.append(old_array[index2]); \
-new_array.append(old_array[index3]);
+#define GLTF_APPEND_ARRAY_1(newArray, oldArray) GLTF_GET_INDICIES(1) \
+newArray.append(oldArray[index1]); \
+newArray.append(oldArray[index2]); \
+newArray.append(oldArray[index3]);
 
-#define GLTF_APPEND_ARRAY_2(new_array, old_array) GLTF_GET_INDICIES(2) \
-new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); \
-new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); \
-new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]);
+#define GLTF_APPEND_ARRAY_2(newArray, oldArray) GLTF_GET_INDICIES(2) \
+newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); \
+newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); \
+newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]);
 
-#define GLTF_APPEND_ARRAY_3(new_array, old_array) GLTF_GET_INDICIES(3) \
-new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); new_array.append(old_array[index1 + 2]); \
-new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); new_array.append(old_array[index2 + 2]); \
-new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]); new_array.append(old_array[index3 + 2]);
+#define GLTF_APPEND_ARRAY_3(newArray, oldArray) GLTF_GET_INDICIES(3) \
+newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); \
+newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); \
+newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]);
 
-#define GLTF_APPEND_ARRAY_4(new_array, old_array) GLTF_GET_INDICIES(4) \
-new_array.append(old_array[index1]); new_array.append(old_array[index1 + 1]); new_array.append(old_array[index1 + 2]); new_array.append(old_array[index1 + 3]); \
-new_array.append(old_array[index2]); new_array.append(old_array[index2 + 1]); new_array.append(old_array[index2 + 2]); new_array.append(old_array[index2 + 3]); \
-new_array.append(old_array[index3]); new_array.append(old_array[index3 + 1]); new_array.append(old_array[index3 + 2]); new_array.append(old_array[index3 + 3]);
+#define GLTF_APPEND_ARRAY_4(newArray, oldArray) GLTF_GET_INDICIES(4) \
+newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); newArray.append(oldArray[index1 + 3]); \
+newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); newArray.append(oldArray[index2 + 3]); \
+newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); newArray.append(oldArray[index3 + 3]);
 
 bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname,
                               QString& value, QMap<QString, bool>&  defined) {
@@ -997,7 +997,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
         hfmModel.materials[matid] = HFMMaterial();
         HFMMaterial& hfmMaterial = hfmModel.materials[matid];
         hfmMaterial._material = std::make_shared<graphics::Material>();
-		hfmMaterial.name = hfmMaterial.materialID = matid;
+        hfmMaterial.name = hfmMaterial.materialID = matid;
         setHFMMaterial(hfmMaterial, _file.materials[i]);
     }
 
@@ -1044,15 +1044,15 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 QVector<float> vertices;
                 QVector<float> normals;
                 QVector<float> tangents;
-                int tangent_stride = 0;
+                int tangentStride = 0;
                 QVector<float> texcoords;
                 QVector<float> texcoords2;
                 QVector<float> colors;
-                int color_stride = 0;
+                int colorStride = 0;
                 QVector<uint16_t> joints;
-                int joint_stride = 0;
+                int jointStride = 0;
                 QVector<float> weights;
-                int weight_stride = 0;
+                int weightStride = 0;
 
                 bool success = addArrayOfFromAccessor(indicesAccessor, indices);
 
@@ -1073,7 +1073,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                     GLTFAccessor& accessor = _file.accessors[accessorIdx];
 
-                    int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
                     if (key == "POSITION") {
                         success = addArrayOfFromAccessor(accessor, vertices);
                         if (!success) {
@@ -1104,9 +1103,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         }
 
                         if (accessor.type == GLTFAccessorType::VEC4) {
-                            tangent_stride = 4;
+                            tangentStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
-                            tangent_stride = 3;
+                            tangentStride = 3;
                         } else {
                             qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url;
                             continue;
@@ -1141,9 +1140,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         }
 
                         if (accessor.type == GLTFAccessorType::VEC4) {
-                            color_stride = 4;
+                            colorStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
-                            color_stride = 3;
+                            colorStride = 3;
                         } else {
                             qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url;
                             continue;
@@ -1156,13 +1155,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         }
 
                         if (accessor.type == GLTFAccessorType::VEC4) {
-                            joint_stride = 4;
+                            jointStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
-                            joint_stride = 3;
+                            jointStride = 3;
                         } else if (accessor.type == GLTFAccessorType::VEC2) {
-                            joint_stride = 2;
+                            jointStride = 2;
                         } else if (accessor.type == GLTFAccessorType::SCALAR) {
-                            joint_stride = 1;
+                            jointStride = 1;
                         } else {
                             qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url;
                             continue;
@@ -1175,13 +1174,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         }
 
                         if (accessor.type == GLTFAccessorType::VEC4) {
-                            weight_stride = 4;
+                            weightStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
-                            weight_stride = 3;
+                            weightStride = 3;
                         } else if (accessor.type == GLTFAccessorType::VEC2) {
-                            weight_stride = 2;
+                            weightStride = 2;
                         } else if (accessor.type == GLTFAccessorType::SCALAR) {
-                            weight_stride = 1;
+                            weightStride = 1;
                         } else {
                             qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url;
                             continue;
@@ -1189,101 +1188,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     }
                 }
 
-                // generate the normals if they don't exist
-                if (normals.size() == 0) {
-                    QVector<int> new_indices;
-                    QVector<float> new_vertices;
-                    QVector<float> new_normals;
-                    QVector<float> new_tangents;
-                    QVector<float> new_texcoords;
-                    QVector<float> new_texcoords2;
-                    QVector<float> new_colors;
-                    QVector<uint16_t> new_joints;
-                    QVector<float> new_weights;
-
-                    for (int n = 0; n < indices.size(); n = n + 3) {
-                        int v1_index = (indices[n + 0] * 3);
-                        int v2_index = (indices[n + 1] * 3);
-                        int v3_index = (indices[n + 2] * 3);
-
-                        glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]);
-                        glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]);
-                        glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]);
-
-                        new_vertices.append(v1.x); new_vertices.append(v1.y); new_vertices.append(v1.z);
-                        new_vertices.append(v2.x); new_vertices.append(v2.y); new_vertices.append(v2.z);
-                        new_vertices.append(v3.x); new_vertices.append(v3.y); new_vertices.append(v3.z);
-
-                        glm::vec3 norm = glm::normalize(glm::cross(v2 - v1, v3 - v1));
-
-                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
-                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
-                        new_normals.append(norm.x); new_normals.append(norm.y); new_normals.append(norm.z);
-
-                        if (tangents.size() > 0) {
-							if (tangent_stride == 4) {
-                                GLTF_APPEND_ARRAY_4(new_tangents, tangents)
-                            } else {
-                                GLTF_APPEND_ARRAY_3(new_tangents, tangents)
-                            }
-                        }
-
-                        if (texcoords.size() > 0) {
-                            GLTF_APPEND_ARRAY_2(new_texcoords, texcoords)
-                        }
-
-                        if (texcoords2.size() > 0) {
-                            GLTF_APPEND_ARRAY_2(new_texcoords2, texcoords2)
-                        }
-
-                        if (colors.size() > 0) {
-                            if (color_stride == 4) {
-                                GLTF_APPEND_ARRAY_4(new_colors, colors)
-                            } else {
-                                GLTF_APPEND_ARRAY_3(new_colors, colors)
-                            }
-                        }
-
-                        if (joints.size() > 0) {
-                            if (joint_stride == 4) {
-                                GLTF_APPEND_ARRAY_4(new_joints, joints)
-                            } else if (joint_stride == 3) {
-                                GLTF_APPEND_ARRAY_3(new_joints, joints)
-                            } else if (joint_stride == 2) {
-                                GLTF_APPEND_ARRAY_2(new_joints, joints)
-                            } else {
-                                GLTF_APPEND_ARRAY_1(new_joints, joints)
-                            }
-                        }
-
-                        if (weights.size() > 0) {
-                            if (weight_stride == 4) {
-                                GLTF_APPEND_ARRAY_4(new_weights, weights)
-                            } else if (weight_stride == 3) {
-                                GLTF_APPEND_ARRAY_3(new_weights, weights)
-                            } else if (weight_stride == 2) {
-                                GLTF_APPEND_ARRAY_2(new_weights, weights)
-                            } else {
-                                GLTF_APPEND_ARRAY_1(new_weights, weights)
-                            }
-                        }
-
-                        new_indices.append(n);
-                        new_indices.append(n + 1);
-                        new_indices.append(n + 2);
-                    }
-
-                    vertices = new_vertices;
-                    normals = new_normals;
-                    tangents = new_tangents;
-                    indices = new_indices;
-                    texcoords = new_texcoords;
-                    texcoords2 = new_texcoords2;
-                    colors = new_colors;
-                    joints = new_joints;
-                    weights = new_weights;
-                }
-
                 for (int n = 0; n < indices.count(); n++) {
                     part.triangleIndices.push_back(indices[n] + prevMeshVerticesCount);
                 }
@@ -1296,8 +1200,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
                 }
 
-                for (int n = 0; n < tangents.size(); n += tangent_stride) {
-                    float tanW = tangent_stride == 4 ? tangents[n + 3] : 1;
+                for (int n = 0; n < tangents.size(); n += tangentStride) {
+                    float tanW = tangentStride == 4 ? tangents[n + 3] : 1;
                     mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
                 }
 
@@ -1308,17 +1212,17 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1]));
                 }
 
-                for (int n = 0; n < colors.size(); n += color_stride) {
+                for (int n = 0; n < colors.size(); n += colorStride) {
                     mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
                 }
 
-                for (int n = 0; n < joints.size(); n += joint_stride) {
+                for (int n = 0; n < joints.size(); n += jointStride) {
                     clusterJoints.push_back(joints[n]);
-                    if (joint_stride > 1) {
+                    if (jointStride > 1) {
                         clusterJoints.push_back(joints[n + 1]);
-                        if (joint_stride > 2) {
+                        if (jointStride > 2) {
                             clusterJoints.push_back(joints[n + 2]);
-                            if (joint_stride > 3) {
+                            if (jointStride > 3) {
                                 clusterJoints.push_back(joints[n + 3]);
                             } else {
                                 clusterJoints.push_back(0);
@@ -1334,13 +1238,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     }
                 }
 
-                for (int n = 0; n < weights.size(); n += weight_stride) {
+                for (int n = 0; n < weights.size(); n += weightStride) {
                     clusterWeights.push_back(weights[n]);
-                    if (weight_stride > 1) {
+                    if (weightStride > 1) {
                         clusterWeights.push_back(weights[n + 1]);
-                        if (weight_stride > 2) {
+                        if (weightStride > 2) {
                             clusterWeights.push_back(weights[n + 2]);
-                            if (weight_stride > 3) {
+                            if (weightStride > 3) {
                                 clusterWeights.push_back(weights[n + 3]);
                             } else {
                                 clusterWeights.push_back(0);

From a1279fa0b16b1b608450aec074477e69b0ccf492 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Mon, 22 Apr 2019 21:10:12 +0100
Subject: [PATCH 04/10] Fixed requested indentation issues.

# Conflicts:
#	libraries/fbx/src/GLTFSerializer.cpp
---
 libraries/fbx/src/GLTFSerializer.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index e7b9dd79f7..3f569a3c3e 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -872,7 +872,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
             // collect transforms for a node's parents, grandparents, etc.
             _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode));
             parentIndex = parents[parentIndex];
-            }
+        }
         nodecount++;
     }
 
@@ -900,10 +900,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     sortedNodes[j] = currentNode;
                     i++;
                     currentNode = sortedNodes[i];
-    }
+                }
                 j++;
-    }
-    }
+            }
+        }
     }
 
 
@@ -931,7 +931,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
         joint.translation = extractTranslation(joint.transform);
         joint.rotation = glmExtractRotation(joint.transform);
         glm::vec3 scale = extractScale(joint.transform);
-        joint.postTransform = glm::scale(glm::mat4(), scale);        
+        joint.postTransform = glm::scale(glm::mat4(), scale);
 
         joint.name = node.name;
         joint.isSkeletonJoint = false;
@@ -982,7 +982,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
     QString unknown = "Default";
     int ukcount = 0;
     foreach(auto material, _file.materials) {
-        if (!(material.defined["name"])) {
+        if (!material.defined["name"]) {
             QString name = unknown + QString::number(ukcount++);
             material.name = name;
             material.defined.insert("name", true);
@@ -1017,8 +1017,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 cluster.inverseBindMatrix = glm::mat4();
                 cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
                 mesh.clusters.append(cluster);
-                } else { // skinned model
-                    for (int j = 0; j < numNodes; j++) {
+            } else { // skinned model
+                for (int j = 0; j < numNodes; j++) {
                     HFMCluster cluster;
                     cluster.jointIndex = j;
                     cluster.inverseBindMatrix = jointInverseBindTransforms[j];
@@ -1026,7 +1026,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     mesh.clusters.append(cluster);
                 }
             }
-            HFMCluster root; 
+            HFMCluster root;
             root.jointIndex = 0;
             root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
             root.inverseBindTransform = Transform(root.inverseBindMatrix);

From 9b7df26d9195ff00b5fb7dd6b961284e6ea594ba Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Sat, 27 Apr 2019 11:20:09 +0100
Subject: [PATCH 05/10] Fixed cluster weighting regression.

---
 libraries/fbx/src/GLTFSerializer.cpp | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 3f569a3c3e..7c13d58968 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -1262,15 +1262,20 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                 // Build weights (adapted from FBXSerializer.cpp)
                 if (hfmModel.hasSkeletonJoints) {
-                    int numClusterIndices = clusterJoints.size();
+                    int prevMeshClusterIndexCount = mesh.clusterIndices.count();
+                    int prevMeshClusterWeightCount = mesh.clusterWeights.count();
                     const int WEIGHTS_PER_VERTEX = 4;
                     const float ALMOST_HALF = 0.499f;
-                    int numVertices = mesh.vertices.size();
-                    mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices);
-                    mesh.clusterWeights.fill(0, numClusterIndices);
+                    int numVertices = mesh.vertices.size() - prevMeshVerticesCount;
+
+                    // Append new cluster indices and weights for this mesh part
+                    for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; i++) {
+                        mesh.clusterIndices.push_back(mesh.clusters.size() - 1);
+                        mesh.clusterWeights.push_back(0);
+                    }
 
                     for (int c = 0; c < clusterJoints.size(); c++) {
-                        mesh.clusterIndices[c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]];
+                        mesh.clusterIndices[prevMeshClusterIndexCount+c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]];
                     }
 
                     // normalize and compress to 16-bits
@@ -1284,10 +1289,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                         if (totalWeight > 0.0f) {
                             float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
                             for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
-                                mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
+                                mesh.clusterWeights[prevMeshClusterWeightCount+k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
                             }
                         } else {
-                            mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
+                            mesh.clusterWeights[prevMeshClusterWeightCount+j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
                         }
                     }
                 }

From 0bf216ab730c91fcc98bd02cf5e0db9f6aec11dc Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Sat, 27 Apr 2019 12:42:37 +0100
Subject: [PATCH 06/10] Sparse accessors support for skin inverse bind matrices
 and shapekey target data.

---
 libraries/fbx/src/GLTFSerializer.cpp | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 7c13d58968..f5cc9ff03c 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -812,32 +812,16 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
 void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) {
     for (auto &skin : _file.skins) {
         GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
-        GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
-        GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer];
-        int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
         QVector<float> matrices;
-        addArrayOfType(indicesBuffer.blob, 
-            indicesBufferview.byteOffset + accBoffset, 
-            indicesAccessor.count, 
-            matrices,
-            indicesAccessor.type, 
-            indicesAccessor.componentType);
+        addArrayOfFromAccessor(indicesAccessor, matrices);
         inverseBindMatrixValues.push_back(matrices.toStdVector());
     }
 }
 
 void GLTFSerializer::generateTargetData(int index, float weight, QVector<glm::vec3>& returnVector) {
     GLTFAccessor& accessor = _file.accessors[index];
-    GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView];
-    GLTFBuffer& buffer = _file.buffers[bufferview.buffer];
-    int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
     QVector<float> storedValues;
-    addArrayOfType(buffer.blob, 
-        bufferview.byteOffset + accBoffset, 
-        accessor.count, 
-        storedValues,
-        accessor.type,
-        accessor.componentType);
+    addArrayOfFromAccessor(accessor, storedValues);
     for (int n = 0; n < storedValues.size(); n = n + 3) {
         returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2]));
     }

From 95ec51e37bb27b6d273433bb32f149285ac150d1 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Mon, 29 Apr 2019 20:35:38 +0100
Subject: [PATCH 07/10] Formatting and naming changes. Tweak to structure of
 skinning code.

---
 libraries/fbx/src/GLTFSerializer.cpp | 49 ++++++++++++++--------------
 libraries/fbx/src/GLTFSerializer.h   |  2 +-
 2 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index f5cc9ff03c..99211fb6fa 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -813,7 +813,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
     for (auto &skin : _file.skins) {
         GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
         QVector<float> matrices;
-        addArrayOfFromAccessor(indicesAccessor, matrices);
+        addArrayFromAccessor(indicesAccessor, matrices);
         inverseBindMatrixValues.push_back(matrices.toStdVector());
     }
 }
@@ -821,7 +821,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
 void GLTFSerializer::generateTargetData(int index, float weight, QVector<glm::vec3>& returnVector) {
     GLTFAccessor& accessor = _file.accessors[index];
     QVector<float> storedValues;
-    addArrayOfFromAccessor(accessor, storedValues);
+    addArrayFromAccessor(accessor, storedValues);
     for (int n = 0; n < storedValues.size(); n = n + 3) {
         returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2]));
     }
@@ -849,7 +849,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
     nodecount = 0;
     foreach(auto &node, _file.nodes) {
         // collect node transform
-        _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); 
+        _file.nodes[nodecount].transforms.push_back(getModelTransform(node));
         int parentIndex = parents[nodecount];
         while (parentIndex != -1) {
             const auto& parentNode = _file.nodes[parentIndex];
@@ -1038,7 +1038,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 QVector<float> weights;
                 int weightStride = 0;
 
-                bool success = addArrayOfFromAccessor(indicesAccessor, indices);
+                bool success = addArrayFromAccessor(indicesAccessor, indices);
 
                 if (!success) {
                     qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url;
@@ -1058,7 +1058,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     GLTFAccessor& accessor = _file.accessors[accessorIdx];
 
                     if (key == "POSITION") {
-                        success = addArrayOfFromAccessor(accessor, vertices);
+                        success = addArrayFromAccessor(accessor, vertices);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url;
                             continue;
@@ -1069,7 +1069,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "NORMAL") {
-                        success = addArrayOfFromAccessor(accessor, normals);
+                        success = addArrayFromAccessor(accessor, normals);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url;
                             continue;
@@ -1080,7 +1080,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "TANGENT") {
-                        success = addArrayOfFromAccessor(accessor, tangents);
+                        success = addArrayFromAccessor(accessor, tangents);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
                             continue;
@@ -1095,7 +1095,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "TEXCOORD_0") {
-                        success = addArrayOfFromAccessor(accessor, texcoords);
+                        success = addArrayFromAccessor(accessor, texcoords);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url;
                             continue;
@@ -1106,7 +1106,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "TEXCOORD_1") {
-                        success = addArrayOfFromAccessor(accessor, texcoords2);
+                        success = addArrayFromAccessor(accessor, texcoords2);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url;
                             continue;
@@ -1117,7 +1117,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "COLOR_0") {
-                        success = addArrayOfFromAccessor(accessor, colors);
+                        success = addArrayFromAccessor(accessor, colors);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
                             continue;
@@ -1132,7 +1132,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "JOINTS_0") {
-                        success = addArrayOfFromAccessor(accessor, joints);
+                        success = addArrayFromAccessor(accessor, joints);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
                             continue;
@@ -1151,7 +1151,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "WEIGHTS_0") {
-                        success = addArrayOfFromAccessor(accessor, weights);
+                        success = addArrayFromAccessor(accessor, weights);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
                             continue;
@@ -1252,15 +1252,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     const float ALMOST_HALF = 0.499f;
                     int numVertices = mesh.vertices.size() - prevMeshVerticesCount;
 
-                    // Append new cluster indices and weights for this mesh part
-                    for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; i++) {
-                        mesh.clusterIndices.push_back(mesh.clusters.size() - 1);
-                        mesh.clusterWeights.push_back(0);
-                    }
-
-                    for (int c = 0; c < clusterJoints.size(); c++) {
-                        mesh.clusterIndices[prevMeshClusterIndexCount+c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]];
-                    }
+                    mesh.clusterIndices.resize(mesh.clusterIndices.size()
+                        + numVertices * WEIGHTS_PER_VERTEX);
+                    mesh.clusterWeights.resize(mesh.clusterWeights.size()
+                        + numVertices * WEIGHTS_PER_VERTEX);
 
                     // normalize and compress to 16-bits
                     for (int i = 0; i < numVertices; ++i) {
@@ -1268,15 +1263,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                         float totalWeight = 0.0f;
                         for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
+                            mesh.clusterIndices[prevMeshClusterIndexCount + k] =
+                                originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[k]]];
+
                             totalWeight += clusterWeights[k];
                         }
                         if (totalWeight > 0.0f) {
                             float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
                             for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
-                                mesh.clusterWeights[prevMeshClusterWeightCount+k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
+                                mesh.clusterWeights[prevMeshClusterWeightCount + k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
                             }
                         } else {
-                            mesh.clusterWeights[prevMeshClusterWeightCount+j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
+                            mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
+                            for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) {
+                                mesh.clusterWeights[prevMeshClusterWeightCount + k] = 0;
+                            }
                         }
                     }
                 }
@@ -1678,7 +1679,7 @@ bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset,
 }
 
 template <typename T>
-bool GLTFSerializer::addArrayOfFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray) {
+bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray) {
     bool success = true;
 
     if (accessor.defined["bufferView"]) {
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index e7dc76e084..4d72805863 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -841,7 +841,7 @@ private:
                         QVector<T>& outarray, int accessorType, int componentType);
 
     template <typename T>
-    bool addArrayOfFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray);
+    bool addArrayFromAccessor(GLTFAccessor& accessor, QVector<T>& outarray);
 
     void retriangulate(const QVector<int>& in_indices, const QVector<glm::vec3>& in_vertices, 
                        const QVector<glm::vec3>& in_normals, QVector<int>& out_indices, 

From a07507430335e47755dc5c1919350eb66f02c5ba Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Mon, 29 Apr 2019 21:26:13 +0100
Subject: [PATCH 08/10] Revert to previous clusterIndices and clusterWeight
 expansion implementation.

---
 libraries/fbx/src/GLTFSerializer.cpp | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 99211fb6fa..85e55f226b 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -1252,10 +1252,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     const float ALMOST_HALF = 0.499f;
                     int numVertices = mesh.vertices.size() - prevMeshVerticesCount;
 
-                    mesh.clusterIndices.resize(mesh.clusterIndices.size()
-                        + numVertices * WEIGHTS_PER_VERTEX);
-                    mesh.clusterWeights.resize(mesh.clusterWeights.size()
-                        + numVertices * WEIGHTS_PER_VERTEX);
+                    // Append new cluster indices and weights for this mesh part
+                    for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; i++) {
+                        mesh.clusterIndices.push_back(mesh.clusters.size() - 1);
+                        mesh.clusterWeights.push_back(0);
+                    }
+
+                    for (int c = 0; c < clusterJoints.size(); c++) {
+                        mesh.clusterIndices[prevMeshClusterIndexCount + c] =
+                            originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]];
+                    }
 
                     // normalize and compress to 16-bits
                     for (int i = 0; i < numVertices; ++i) {
@@ -1263,9 +1269,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
 
                         float totalWeight = 0.0f;
                         for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
-                            mesh.clusterIndices[prevMeshClusterIndexCount + k] =
-                                originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[k]]];
-
                             totalWeight += clusterWeights[k];
                         }
                         if (totalWeight > 0.0f) {
@@ -1275,9 +1278,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             }
                         } else {
                             mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
-                            for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) {
-                                mesh.clusterWeights[prevMeshClusterWeightCount + k] = 0;
-                            }
                         }
                     }
                 }

From 7987ed14b09bf219f692e4677ccd2208c58ec26d Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Sat, 11 May 2019 04:30:14 +0100
Subject: [PATCH 09/10] Fix mismatched meshpart attributes (generates dummy
 buffers), re-added normals generation routine, buffer size checks, detect
 out-of-range indices,

---
 libraries/fbx/src/GLTFSerializer.cpp | 340 +++++++++++++++++++++------
 1 file changed, 267 insertions(+), 73 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 85e55f226b..b06b8f1822 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -1016,6 +1016,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
             root.inverseBindTransform = Transform(root.inverseBindMatrix);
             mesh.clusters.append(root);
 
+            QList<QString> meshAttributes;
+            foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
+                QList<QString> keys = primitive.attributes.values.keys();
+                foreach (auto &key, keys) {
+                    if (!meshAttributes.contains(key)) {
+                        meshAttributes.push_back(key);
+                    }
+                }
+            }
+
             foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
                 HFMMeshPart part = HFMMeshPart();
 
@@ -1026,17 +1036,21 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                 // Buffers
                 QVector<int> indices;
                 QVector<float> vertices;
+                int verticesStride = 3;
                 QVector<float> normals;
+                int normalStride = 3;
                 QVector<float> tangents;
-                int tangentStride = 0;
+                int tangentStride = 4;
                 QVector<float> texcoords;
+                int texCoordStride = 2;
                 QVector<float> texcoords2;
+                int texCoord2Stride = 2;
                 QVector<float> colors;
-                int colorStride = 0;
+                int colorStride = 3;
                 QVector<uint16_t> joints;
-                int jointStride = 0;
+                int jointStride = 4;
                 QVector<float> weights;
-                int weightStride = 0;
+                int weightStride = 4;
 
                 bool success = addArrayFromAccessor(indicesAccessor, indices);
 
@@ -1058,34 +1072,28 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     GLTFAccessor& accessor = _file.accessors[accessorIdx];
 
                     if (key == "POSITION") {
+                        if (accessor.type != GLTFAccessorType::VEC3) {
+                            qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url;
+                            continue;
+                        }
+
                         success = addArrayFromAccessor(accessor, vertices);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url;
                             continue;
                         }
-
+                    } else if (key == "NORMAL") {
                         if (accessor.type != GLTFAccessorType::VEC3) {
-                            qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url;
+                            qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url;
                             continue;
                         }
-                    } else if (key == "NORMAL") {
+
                         success = addArrayFromAccessor(accessor, normals);
                         if (!success) {
                             qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url;
                             continue;
                         }
-
-                        if (accessor.type != GLTFAccessorType::VEC3) {
-                            qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url;
-                            continue;
-                        }
                     } else if (key == "TANGENT") {
-                        success = addArrayFromAccessor(accessor, tangents);
-                        if (!success) {
-                            qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
-                            continue;
-                        }
-
                         if (accessor.type == GLTFAccessorType::VEC4) {
                             tangentStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
@@ -1094,6 +1102,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url;
                             continue;
                         }
+
+                        success = addArrayFromAccessor(accessor, tangents);
+                        if (!success) {
+                            qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
+                            tangentStride = 0;
+                            continue;
+                        }
                     } else if (key == "TEXCOORD_0") {
                         success = addArrayFromAccessor(accessor, texcoords);
                         if (!success) {
@@ -1117,12 +1132,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             continue;
                         }
                     } else if (key == "COLOR_0") {
-                        success = addArrayFromAccessor(accessor, colors);
-                        if (!success) {
-                            qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
-                            continue;
-                        }
-
                         if (accessor.type == GLTFAccessorType::VEC4) {
                             colorStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
@@ -1131,13 +1140,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url;
                             continue;
                         }
-                    } else if (key == "JOINTS_0") {
-                        success = addArrayFromAccessor(accessor, joints);
+
+                        success = addArrayFromAccessor(accessor, colors);
                         if (!success) {
-                            qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
+                            qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
                             continue;
                         }
-
+                    } else if (key == "JOINTS_0") {
                         if (accessor.type == GLTFAccessorType::VEC4) {
                             jointStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
@@ -1150,13 +1159,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url;
                             continue;
                         }
-                    } else if (key == "WEIGHTS_0") {
-                        success = addArrayFromAccessor(accessor, weights);
+
+                        success = addArrayFromAccessor(accessor, joints);
                         if (!success) {
-                            qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
+                            qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
                             continue;
                         }
-
+                    } else if (key == "WEIGHTS_0") {
                         if (accessor.type == GLTFAccessorType::VEC4) {
                             weightStride = 4;
                         } else if (accessor.type == GLTFAccessorType::VEC3) {
@@ -1169,78 +1178,263 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                             qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url;
                             continue;
                         }
+
+                        success = addArrayFromAccessor(accessor, weights);
+                        if (!success) {
+                            qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
+                            continue;
+                        }
                     }
                 }
 
-                for (int n = 0; n < indices.count(); n++) {
-                    part.triangleIndices.push_back(indices[n] + prevMeshVerticesCount);
+                // Validation stage
+                if (indices.count() == 0) {
+                    qWarning(modelformat) << "Missing indices for model " << _url;
+                    continue;
+                }
+                if (vertices.count() == 0) {
+                    qWarning(modelformat) << "Missing vertices for model " << _url;
+                    continue;
                 }
 
-                for (int n = 0; n < vertices.size(); n = n + 3) {
+                int partVerticesCount = vertices.size() / 3;
+
+                // generate the normals if they don't exist
+                if (normals.size() == 0) {
+                    QVector<int> newIndices;
+                    QVector<float> newVertices;
+                    QVector<float> newNormals;
+                    QVector<float> newTexcoords;
+                    QVector<float> newTexcoords2;
+                    QVector<float> newColors;
+                    QVector<uint16_t> newJoints;
+                    QVector<float> newWeights;
+
+                    for (int n = 0; n < indices.size(); n = n + 3) {
+                        int v1_index = (indices[n + 0] * 3);
+                        int v2_index = (indices[n + 1] * 3);
+                        int v3_index = (indices[n + 2] * 3);
+
+                        glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]);
+                        glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]);
+                        glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]);
+
+                        newVertices.append(v1.x);
+                        newVertices.append(v1.y);
+                        newVertices.append(v1.z);
+                        newVertices.append(v2.x);
+                        newVertices.append(v2.y);
+                        newVertices.append(v2.z);
+                        newVertices.append(v3.x);
+                        newVertices.append(v3.y);
+                        newVertices.append(v3.z);
+
+                        glm::vec3 norm = glm::normalize(glm::cross(v2 - v1, v3 - v1));
+
+                        newNormals.append(norm.x);
+                        newNormals.append(norm.y);
+                        newNormals.append(norm.z);
+                        newNormals.append(norm.x);
+                        newNormals.append(norm.y);
+                        newNormals.append(norm.z);
+                        newNormals.append(norm.x);
+                        newNormals.append(norm.y);
+                        newNormals.append(norm.z);
+
+                        if (texcoords.size() == partVerticesCount * texCoordStride) {
+                            GLTF_APPEND_ARRAY_2(newTexcoords, texcoords)
+                        }
+
+                        if (texcoords2.size() == partVerticesCount * texCoord2Stride) {
+                            GLTF_APPEND_ARRAY_2(newTexcoords2, texcoords2)
+                        }
+
+                        if (colors.size() == partVerticesCount * colorStride) {
+                            if (colorStride == 4) {
+                                GLTF_APPEND_ARRAY_4(newColors, colors)
+                            } else {
+                                GLTF_APPEND_ARRAY_3(newColors, colors)
+                            }
+                        }
+
+                        if (joints.size() == partVerticesCount * jointStride) {
+                            if (jointStride == 4) {
+                                GLTF_APPEND_ARRAY_4(newJoints, joints)
+                            } else if (jointStride == 3) {
+                                GLTF_APPEND_ARRAY_3(newJoints, joints)
+                            } else if (jointStride == 2) {
+                                GLTF_APPEND_ARRAY_2(newJoints, joints)
+                            } else {
+                                GLTF_APPEND_ARRAY_1(newJoints, joints)
+                            }
+                        }
+
+                        if (weights.size() == partVerticesCount * weightStride) {
+                            if (weightStride == 4) {
+                                GLTF_APPEND_ARRAY_4(newWeights, weights)
+                            } else if (weightStride == 3) {
+                                GLTF_APPEND_ARRAY_3(newWeights, weights)
+                            } else if (weightStride == 2) {
+                                GLTF_APPEND_ARRAY_2(newWeights, weights)
+                            } else {
+                                GLTF_APPEND_ARRAY_1(newWeights, weights)
+                            }
+                        }
+                        newIndices.append(n);
+                        newIndices.append(n + 1);
+                        newIndices.append(n + 2);
+                    }
+
+                    vertices = newVertices;
+                    normals = newNormals;
+                    tangents = QVector<float>();
+                    texcoords = newTexcoords;
+                    texcoords2 = newTexcoords2;
+                    colors = newColors;
+                    joints = newJoints;
+                    weights = newWeights;
+                    indices = newIndices;
+
+                    partVerticesCount = vertices.size() / 3;
+                }
+
+                QVector<int> validatedIndices;
+                for (int n = 0; n < indices.count(); n++) {
+                    if (indices[n] < partVerticesCount) {
+                        validatedIndices.push_back(indices[n] + prevMeshVerticesCount);
+                    } else {
+                        validatedIndices = QVector<int>();
+                        break;
+                    }
+                }
+
+                if (validatedIndices.size() == 0) {
+                    qWarning(modelformat) << "Indices out of range for model " << _url;
+                    continue;
+                }
+
+                part.triangleIndices.append(validatedIndices);
+
+                for (int n = 0; n < vertices.size(); n = n + verticesStride) {
                     mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2]));
                 }
 
-                for (int n = 0; n < normals.size(); n = n + 3) {
+                for (int n = 0; n < normals.size(); n = n + normalStride) {
                     mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
                 }
 
-                for (int n = 0; n < tangents.size(); n += tangentStride) {
-                    float tanW = tangentStride == 4 ? tangents[n + 3] : 1;
-                    mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
+                // TODO: add correct tangent generation
+                if (tangents.size() == partVerticesCount * tangentStride) {
+                    for (int n = 0; n < tangents.size(); n += tangentStride) {
+                        float tanW = tangentStride == 4 ? tangents[n + 3] : 1;
+                        mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2]));
+                    }
+                } else {
+                    if (meshAttributes.contains("TANGENT")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            mesh.tangents.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
+                        }
+                    }
                 }
 
-                for (int n = 0; n < texcoords.size(); n = n + 2) {
-                    mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
-                }
-                for (int n = 0; n < texcoords2.size(); n = n + 2) {
-                    mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1]));
+                if (texcoords.size() == partVerticesCount * texCoordStride) {
+                    for (int n = 0; n < texcoords.size(); n = n + 2) {
+                        mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
+                    }
+                } else {
+                    if (meshAttributes.contains("TEXCOORD_0")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            mesh.texCoords.push_back(glm::vec2(0.0f, 0.0f));
+                        }
+                    }
                 }
 
-                for (int n = 0; n < colors.size(); n += colorStride) {
-                    mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
+                if (texcoords.size() == partVerticesCount * texCoord2Stride) {
+                    for (int n = 0; n < texcoords2.size(); n = n + 2) {
+                        mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1]));
+                    }
+                } else {
+                    if (meshAttributes.contains("TEXCOORD_1")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            mesh.texCoords1.push_back(glm::vec2(0.0f, 0.0f));
+                        }
+                    }
                 }
 
-                for (int n = 0; n < joints.size(); n += jointStride) {
-                    clusterJoints.push_back(joints[n]);
-                    if (jointStride > 1) {
-                        clusterJoints.push_back(joints[n + 1]);
-                        if (jointStride > 2) {
-                            clusterJoints.push_back(joints[n + 2]);
-                            if (jointStride > 3) {
-                                clusterJoints.push_back(joints[n + 3]);
+                if (colors.size() == partVerticesCount * colorStride) {
+                    for (int n = 0; n < colors.size(); n += colorStride) {
+                        mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
+                    }
+                } else {
+                    if (meshAttributes.contains("COLOR_0")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            mesh.colors.push_back(glm::vec3(1.0f, 1.0f, 1.0f));
+                        }
+                    }
+                }
+
+                if (joints.size() == partVerticesCount * jointStride) {
+                    for (int n = 0; n < joints.size(); n += jointStride) {
+                        clusterJoints.push_back(joints[n]);
+                        if (jointStride > 1) {
+                            clusterJoints.push_back(joints[n + 1]);
+                            if (jointStride > 2) {
+                                clusterJoints.push_back(joints[n + 2]);
+                                if (jointStride > 3) {
+                                    clusterJoints.push_back(joints[n + 3]);
+                                } else {
+                                    clusterJoints.push_back(0);
+                                }
                             } else {
                                 clusterJoints.push_back(0);
+                                clusterJoints.push_back(0);
                             }
                         } else {
                             clusterJoints.push_back(0);
                             clusterJoints.push_back(0);
+                            clusterJoints.push_back(0);
+                        }
+                    }
+                } else {
+                    if (meshAttributes.contains("JOINTS_0")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            for (int j = 0; j < 4; j++) {
+                                clusterJoints.push_back(0);
+                            }
                         }
-                    } else {
-                        clusterJoints.push_back(0);
-                        clusterJoints.push_back(0);
-                        clusterJoints.push_back(0);
                     }
                 }
 
-                for (int n = 0; n < weights.size(); n += weightStride) {
-                    clusterWeights.push_back(weights[n]);
-                    if (weightStride > 1) {
-                        clusterWeights.push_back(weights[n + 1]);
-                        if (weightStride > 2) {
-                            clusterWeights.push_back(weights[n + 2]);
-                            if (weightStride > 3) {
-                                clusterWeights.push_back(weights[n + 3]);
+                if (weights.size() == partVerticesCount * weightStride) {
+                    for (int n = 0; n < weights.size(); n += weightStride) {
+                        clusterWeights.push_back(weights[n]);
+                        if (weightStride > 1) {
+                            clusterWeights.push_back(weights[n + 1]);
+                            if (weightStride > 2) {
+                                clusterWeights.push_back(weights[n + 2]);
+                                if (weightStride > 3) {
+                                    clusterWeights.push_back(weights[n + 3]);
+                                } else {
+                                    clusterWeights.push_back(0.0f);
+                                }
                             } else {
-                                clusterWeights.push_back(0);
+                                clusterWeights.push_back(0.0f);
+                                clusterWeights.push_back(0.0f);
                             }
                         } else {
-                            clusterWeights.push_back(0);
-                            clusterWeights.push_back(0);
+                            clusterWeights.push_back(0.0f);
+                            clusterWeights.push_back(0.0f);
+                            clusterWeights.push_back(0.0f);
+                        }
+                    }
+                } else {
+                    if (meshAttributes.contains("WEIGHTS_0")) {
+                        for (int i = 0; i < partVerticesCount; i++) {
+                            clusterWeights.push_back(1.0f);
+                            for (int j = 1; j < 4; j++) {
+                                clusterWeights.push_back(0.0f);
+                            }
                         }
-                    } else {
-                        clusterWeights.push_back(0);
-                        clusterWeights.push_back(0);
-                        clusterWeights.push_back(0);
                     }
                 }
 

From 9aadea81b3419dc6590ab8ad74e6f6ac39364e06 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Mon, 13 May 2019 19:13:24 +0100
Subject: [PATCH 10/10] Fix to use correct texcoord2 array.

---
 libraries/fbx/src/GLTFSerializer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index b06b8f1822..7650cf04f6 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -1349,7 +1349,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash&
                     }
                 }
 
-                if (texcoords.size() == partVerticesCount * texCoord2Stride) {
+                if (texcoords2.size() == partVerticesCount * texCoord2Stride) {
                     for (int n = 0; n < texcoords2.size(); n = n + 2) {
                         mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1]));
                     }