//// GLTFSerializer.cpp// libraries/model-serializers/src//// Created by Luis Cuenca on 8/30/17.// Copyright 2017 High Fidelity, Inc.// Copyright 2023 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #define CGLTF_IMPLEMENTATION #include "GLTFSerializer.h" #include <QtCore/QBuffer>#include <QtCore/QIODevice>#include <QtCore/QEventLoop>#include <QtCore/qjsondocument.h>#include <QtCore/qjsonobject.h>#include <QtCore/qjsonarray.h>#include <QtCore/qjsonvalue.h>#include <QtCore/qpair.h>#include <QtCore/qlist.h> #include <QtNetwork/QNetworkAccessManager>#include <QtNetwork/QNetworkRequest> #include <glad/glad.h>#include <qfile.h>#include <qfileinfo.h> #include <sstream> #include <glm/gtx/transform.hpp> #include <shared/NsightHelpers.h>#include <NetworkAccessManager.h>#include <ResourceManager.h>#include <PathUtils.h>#include <image/ColorChannel.h>#include <BlendshapeConstants.h>#include <procedural/ProceduralMaterialCache.h> #include "FBXSerializer.h" float atof_locale_independent(char* str) { //TODO: Once we have C++17 we can use std::from_chars std::istringstream streamToParse(str); streamToParse.imbue(std::locale("C")); float value; if (!(streamToParse >> value)) { qDebug(modelformat) << "cgltf: Cannot parse float from string: " << str; return 0.0f; } return value;} #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(newArray, oldArray) GLTF_GET_INDICIES(1) \newArray.append(oldArray[index1]); \newArray.append(oldArray[index2]); \newArray.append(oldArray[index3]); #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(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(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]); glm::mat4 GLTFSerializer::getModelTransform(const cgltf_node& node) { glm::mat4 tmat = glm::mat4(1.0); if (node.has_matrix) { tmat = glm::mat4(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3], node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7], node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11], node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); } else { if (node.has_scale) { glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); glm::mat4 s = glm::mat4(1.0); s = glm::scale(s, scale); tmat = s * tmat; } if (node.has_rotation) { //quat(x,y,z,w) to quat(w,x,y,z) glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); tmat = glm::mat4_cast(rotquat) * tmat; } if (node.has_translation) { glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); glm::mat4 t = glm::mat4(1.0); t = glm::translate(t, trans); tmat = t * tmat; } } return tmat;} bool GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) { for (size_t i = 0; i < _data->skins_count; i++) { auto &skin = _data->skins[i]; if (skin.inverse_bind_matrices == NULL) { return false; } cgltf_accessor &matricesAccessor = *skin.inverse_bind_matrices; QVector<float> matrices; if (matricesAccessor.type != cgltf_type_mat4) { return false; } matrices.resize((int)matricesAccessor.count * 16); size_t numFloats = cgltf_accessor_unpack_floats(&matricesAccessor, matrices.data(), matricesAccessor.count * 16); Q_ASSERT(numFloats == matricesAccessor.count * 16); inverseBindMatrixValues.push_back(std::vector<float>(matrices.begin(), matrices.end())); } return true;} bool GLTFSerializer::generateTargetData(cgltf_accessor *accessor, float weight, QVector<glm::vec3>& returnVector) { QVector<float> storedValues; if(accessor == nullptr) { return false; } if (accessor->type != cgltf_type_vec3) { return false; } storedValues.resize((int)accessor->count * 3); size_t numFloats = cgltf_accessor_unpack_floats(accessor, storedValues.data(), accessor->count * 3); if (numFloats != accessor->count * 3) { return false; } for (int n = 0; n + 2 < storedValues.size(); n = n + 3) { returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2])); } return true;} bool findNodeInPointerArray(const cgltf_node *nodePointer, cgltf_node **nodes, size_t arraySize, size_t &index) { for (size_t i = 0; i < arraySize; i++) { if (nodes[i] == nodePointer) { index = i; return true; } } return false;} template<typename T> bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, size_t &index) { for (size_t i = 0; i < arraySize; i++) { if (&array[i] == pointer) { index = i; return true; } } return false;} bool findAttribute(const QString &name, const cgltf_attribute *attributes, size_t numAttributes, size_t &index) { std::string nameString = name.toStdString(); for (size_t i = 0; i < numAttributes; i++) { if (attributes[i].name == nullptr) { qDebug(modelformat) << "GLTFSerializer: attribute with a null pointer name string"; } else { if (strcmp(nameString.c_str(), attributes[i].name) == 0) { index = i; return true; } } } return false;} bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { hfmModel.originalURL = url.toString(); int numNodes = (int)_data->nodes_count; //Build dependencies QVector<int> parents; QVector<int> sortedNodes; parents.fill(-1, numNodes); sortedNodes.reserve(numNodes); for(int index = 0; index < numNodes; index++) { auto &node = _data->nodes[index]; for(size_t childIndexInParent = 0; childIndexInParent < node.children_count; childIndexInParent++) { cgltf_node *child = node.children[childIndexInParent]; size_t childIndex = 0; if (!findPointerInArray(child, _data->nodes, _data->nodes_count, childIndex)) { qDebug(modelformat) << "findPointerInArray failed for model: " << _url; hfmModel.loadErrorCount++; return false; } parents[(int)childIndex] = index; } sortedNodes.push_back(index); } // Build transforms typedef QVector<glm::mat4> NodeTransforms; QVector<NodeTransforms> transforms; transforms.resize(numNodes); for (int index = 0; index < numNodes; index++) { // collect node transform auto &node = _data->nodes[index]; transforms[index].push_back(getModelTransform(node)); int parentIndex = parents[index]; while (parentIndex != -1) { const auto& parentNode = _data->nodes[parentIndex]; // collect transforms for a node's parents, grandparents, etc. transforms[index].push_back(getModelTransform(parentNode)); parentIndex = parents[parentIndex]; } } // 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; hasBeenSorted.fill(false, numNodes); { int i = 0; // initial index while (i < numNodes) { int currentNode = sortedNodes[i]; int parentIndex = parents[currentNode]; if (parentIndex == -1 || hasBeenSorted[parentIndex]) { hasBeenSorted[currentNode] = true; ++i; } else { int j = i + 1; // index of node to be sorted while (j < numNodes) { int nextNode = sortedNodes[j]; parentIndex = parents[nextNode]; if (parentIndex == -1 || hasBeenSorted[parentIndex]) { // swap with currentNode hasBeenSorted[nextNode] = true; sortedNodes[i] = nextNode; sortedNodes[j] = currentNode; ++i; currentNode = sortedNodes[i]; } ++j; } } } } // Build map from original to new indices QVector<int> originalToNewNodeIndexMap; originalToNewNodeIndexMap.fill(-1, numNodes); for (int i = 0; i < numNodes; ++i) { originalToNewNodeIndexMap[sortedNodes[i]] = i; } // Build joints HFMJoint joint{}; joint.distanceToParent = 0; hfmModel.jointIndices["x"] = numNodes; QVector<glm::mat4> globalTransforms; globalTransforms.resize(numNodes); for (int nodeIndex : sortedNodes) { auto& node = _data->nodes[nodeIndex]; joint.parentIndex = parents[nodeIndex]; if (joint.parentIndex != -1) { joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.transform = transforms[nodeIndex].first(); joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); joint.postTransform = glm::scale(glm::mat4(), scale); joint.parentIndex = parents[nodeIndex]; globalTransforms[nodeIndex] = joint.transform; if (joint.parentIndex != -1) { globalTransforms[nodeIndex] = globalTransforms[joint.parentIndex] * globalTransforms[nodeIndex]; joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.name = node.name; joint.isSkeletonJoint = false; hfmModel.joints.push_back(joint); } hfmModel.shapeVertices.resize(hfmModel.joints.size()); // get offset transform from mapping float unitScaleFactor = 1.0f; float offsetScale = mapping.value("scale", 1.0f).toFloat() * unitScaleFactor; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); hfmModel.offset = glm::translate(glm::mat4(), glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat())) * glm::mat4_cast(offsetRotation) * glm::scale(glm::mat4(), glm::vec3(offsetScale, offsetScale, offsetScale)); // Build skeleton std::vector<glm::mat4> jointInverseBindTransforms; std::vector<glm::mat4> globalBindTransforms; jointInverseBindTransforms.resize(numNodes); globalBindTransforms.resize(numNodes); hfmModel.hasSkeletonJoints = _data->skins_count > 0; if (hfmModel.hasSkeletonJoints) { std::vector<std::vector<float>> inverseBindValues; if (!getSkinInverseBindMatrices(inverseBindValues)) { qDebug(modelformat) << "GLTFSerializer::getSkinInverseBindMatrices: wrong matrices accessor type for model: " << _url; hfmModel.loadErrorCount++; return false; } for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; for (size_t s = 0; s < _data->skins_count; ++s) { const auto& skin = _data->skins[s]; size_t jointNodeIndex = 0; joint.isSkeletonJoint = findNodeInPointerArray(&_data->nodes[nodeIndex], skin.joints, skin.joints_count, jointNodeIndex); // build inverse bind matrices if (joint.isSkeletonJoint) { size_t matrixIndex = jointNodeIndex; std::vector<float>& value = inverseBindValues[s]; size_t matrixCount = 16 * matrixIndex; if (matrixCount + 15 >= value.size()) { qDebug(modelformat) << "GLTFSerializer::buildGeometry: not enough entries in jointInverseBindTransforms: " << _url; hfmModel.loadErrorCount++; return false; } jointInverseBindTransforms[jointIndex] = glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]); } else { jointInverseBindTransforms[jointIndex] = glm::mat4(); } globalBindTransforms[jointIndex] = jointInverseBindTransforms[jointIndex]; if (joint.parentIndex != -1) { globalBindTransforms[jointIndex] = globalBindTransforms[joint.parentIndex] * globalBindTransforms[jointIndex]; } glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); hfmModel.bindExtents.addPoint(bindTranslation); } hfmModel.joints[jointIndex] = joint; } } // Build materials QVector<QString> materialIDs; QString unknown = "Default"; for (size_t i = 0; i < _data->materials_count; i++) { auto &material = _data->materials[i]; QString mid; if (material.name != nullptr) { mid = QString(material.name); }else{ mid = QString::number(i); } materialIDs.push_back(mid); } for (int i = 0; i < materialIDs.size(); ++i) { QString& matid = materialIDs[i]; hfmModel.materials[matid] = HFMMaterial(); HFMMaterial& hfmMaterial = hfmModel.materials[matid]; hfmMaterial.name = hfmMaterial.materialID = matid; setHFMMaterial(hfmMaterial, _data->materials[i]); } // Build meshes int nodeCount = 0; hfmModel.meshExtents.reset(); for (int nodeIndex : sortedNodes) { auto& node = _data->nodes[nodeIndex]; if (node.mesh != nullptr) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; mesh.modelTransform = globalTransforms[nodeIndex]; 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 root; root.jointIndex = 0; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root); QList<QString> meshAttributes; for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { auto &primitive = node.mesh->primitives[primitiveIndex]; for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { auto &attribute = primitive.attributes[attributeIndex]; QString key(attribute.name); if (!meshAttributes.contains(key)) { meshAttributes.push_back(key); } } } for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { auto &primitive = node.mesh->primitives[primitiveIndex]; HFMMeshPart part = HFMMeshPart(); if (primitive.indices == nullptr) { qDebug() << "No indices accessor for mesh: " << _url; hfmModel.loadErrorCount++; return false; } auto &indicesAccessor = primitive.indices; // Buffers QVector<int> indices; QVector<float> vertices; int verticesStride = 3; QVector<float> normals; int normalStride = 3; QVector<float> tangents; int tangentStride = 4; QVector<float> texcoords; int texCoordStride = 2; QVector<float> texcoords2; int texCoord2Stride = 2; QVector<float> colors; int colorStride = 3; QVector<uint16_t> joints; int jointStride = 4; QVector<float> weights; int weightStride = 4; indices.resize((int)indicesAccessor->count); size_t readIndicesCount = cgltf_accessor_unpack_indices(indicesAccessor, indices.data(), sizeof(unsigned int), indicesAccessor->count); if (readIndicesCount != indicesAccessor->count) { qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; hfmModel.loadErrorCount++; 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(); // For each vertex (stride is WEIGHTS_PER_VERTEX), it contains index of the cluster that given weight belongs to. QVector<uint16_t> clusterJoints; QVector<float> clusterWeights; for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { if (primitive.attributes[attributeIndex].name == nullptr) { qDebug() << "Inalid accessor name for mesh: " << _url; hfmModel.loadErrorCount++; return false; } QString key(primitive.attributes[attributeIndex].name); if (primitive.attributes[attributeIndex].data == nullptr) { qDebug() << "Inalid accessor for mesh: " << _url; hfmModel.loadErrorCount++; return false; } auto accessor = primitive.attributes[attributeIndex].data; int accessorCount = (int)accessor->count; if (key == "POSITION") {Lines 1–500 of 1663. Load more
// // GLTFSerializer.cpp // libraries/model-serializers/src // // Created by Luis Cuenca on 8/30/17. // Copyright 2017 High Fidelity, Inc. // Copyright 2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #define CGLTF_IMPLEMENTATION #include "GLTFSerializer.h" #include <QtCore/QBuffer> #include <QtCore/QIODevice> #include <QtCore/QEventLoop> #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonarray.h> #include <QtCore/qjsonvalue.h> #include <QtCore/qpair.h> #include <QtCore/qlist.h> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkRequest> #include <glad/glad.h> #include <qfile.h> #include <qfileinfo.h> #include <sstream> #include <glm/gtx/transform.hpp> #include <shared/NsightHelpers.h> #include <NetworkAccessManager.h> #include <ResourceManager.h> #include <PathUtils.h> #include <image/ColorChannel.h> #include <BlendshapeConstants.h> #include <procedural/ProceduralMaterialCache.h> #include "FBXSerializer.h" float atof_locale_independent(char* str) { //TODO: Once we have C++17 we can use std::from_chars std::istringstream streamToParse(str); streamToParse.imbue(std::locale("C")); float value; if (!(streamToParse >> value)) { qDebug(modelformat) << "cgltf: Cannot parse float from string: " << str; return 0.0f; } return value; } #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(newArray, oldArray) GLTF_GET_INDICIES(1) \ newArray.append(oldArray[index1]); \ newArray.append(oldArray[index2]); \ newArray.append(oldArray[index3]); #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(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(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]); glm::mat4 GLTFSerializer::getModelTransform(const cgltf_node& node) { glm::mat4 tmat = glm::mat4(1.0); if (node.has_matrix) { tmat = glm::mat4(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3], node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7], node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11], node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); } else { if (node.has_scale) { glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); glm::mat4 s = glm::mat4(1.0); s = glm::scale(s, scale); tmat = s * tmat; } if (node.has_rotation) { //quat(x,y,z,w) to quat(w,x,y,z) glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); tmat = glm::mat4_cast(rotquat) * tmat; } if (node.has_translation) { glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); glm::mat4 t = glm::mat4(1.0); t = glm::translate(t, trans); tmat = t * tmat; } } return tmat; } bool GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) { for (size_t i = 0; i < _data->skins_count; i++) { auto &skin = _data->skins[i]; if (skin.inverse_bind_matrices == NULL) { return false; } cgltf_accessor &matricesAccessor = *skin.inverse_bind_matrices; QVector<float> matrices; if (matricesAccessor.type != cgltf_type_mat4) { return false; } matrices.resize((int)matricesAccessor.count * 16); size_t numFloats = cgltf_accessor_unpack_floats(&matricesAccessor, matrices.data(), matricesAccessor.count * 16); Q_ASSERT(numFloats == matricesAccessor.count * 16); inverseBindMatrixValues.push_back(std::vector<float>(matrices.begin(), matrices.end())); } return true; } bool GLTFSerializer::generateTargetData(cgltf_accessor *accessor, float weight, QVector<glm::vec3>& returnVector) { QVector<float> storedValues; if(accessor == nullptr) { return false; } if (accessor->type != cgltf_type_vec3) { return false; } storedValues.resize((int)accessor->count * 3); size_t numFloats = cgltf_accessor_unpack_floats(accessor, storedValues.data(), accessor->count * 3); if (numFloats != accessor->count * 3) { return false; } for (int n = 0; n + 2 < storedValues.size(); n = n + 3) { returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2])); } return true; } bool findNodeInPointerArray(const cgltf_node *nodePointer, cgltf_node **nodes, size_t arraySize, size_t &index) { for (size_t i = 0; i < arraySize; i++) { if (nodes[i] == nodePointer) { index = i; return true; } } return false; } template<typename T> bool findPointerInArray(const T *pointer, const T *array, size_t arraySize, size_t &index) { for (size_t i = 0; i < arraySize; i++) { if (&array[i] == pointer) { index = i; return true; } } return false; } bool findAttribute(const QString &name, const cgltf_attribute *attributes, size_t numAttributes, size_t &index) { std::string nameString = name.toStdString(); for (size_t i = 0; i < numAttributes; i++) { if (attributes[i].name == nullptr) { qDebug(modelformat) << "GLTFSerializer: attribute with a null pointer name string"; } else { if (strcmp(nameString.c_str(), attributes[i].name) == 0) { index = i; return true; } } } return false; } bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) { hfmModel.originalURL = url.toString(); int numNodes = (int)_data->nodes_count; //Build dependencies QVector<int> parents; QVector<int> sortedNodes; parents.fill(-1, numNodes); sortedNodes.reserve(numNodes); for(int index = 0; index < numNodes; index++) { auto &node = _data->nodes[index]; for(size_t childIndexInParent = 0; childIndexInParent < node.children_count; childIndexInParent++) { cgltf_node *child = node.children[childIndexInParent]; size_t childIndex = 0; if (!findPointerInArray(child, _data->nodes, _data->nodes_count, childIndex)) { qDebug(modelformat) << "findPointerInArray failed for model: " << _url; hfmModel.loadErrorCount++; return false; } parents[(int)childIndex] = index; } sortedNodes.push_back(index); } // Build transforms typedef QVector<glm::mat4> NodeTransforms; QVector<NodeTransforms> transforms; transforms.resize(numNodes); for (int index = 0; index < numNodes; index++) { // collect node transform auto &node = _data->nodes[index]; transforms[index].push_back(getModelTransform(node)); int parentIndex = parents[index]; while (parentIndex != -1) { const auto& parentNode = _data->nodes[parentIndex]; // collect transforms for a node's parents, grandparents, etc. transforms[index].push_back(getModelTransform(parentNode)); parentIndex = parents[parentIndex]; } } // 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; hasBeenSorted.fill(false, numNodes); { int i = 0; // initial index while (i < numNodes) { int currentNode = sortedNodes[i]; int parentIndex = parents[currentNode]; if (parentIndex == -1 || hasBeenSorted[parentIndex]) { hasBeenSorted[currentNode] = true; ++i; } else { int j = i + 1; // index of node to be sorted while (j < numNodes) { int nextNode = sortedNodes[j]; parentIndex = parents[nextNode]; if (parentIndex == -1 || hasBeenSorted[parentIndex]) { // swap with currentNode hasBeenSorted[nextNode] = true; sortedNodes[i] = nextNode; sortedNodes[j] = currentNode; ++i; currentNode = sortedNodes[i]; } ++j; } } } } // Build map from original to new indices QVector<int> originalToNewNodeIndexMap; originalToNewNodeIndexMap.fill(-1, numNodes); for (int i = 0; i < numNodes; ++i) { originalToNewNodeIndexMap[sortedNodes[i]] = i; } // Build joints HFMJoint joint{}; joint.distanceToParent = 0; hfmModel.jointIndices["x"] = numNodes; QVector<glm::mat4> globalTransforms; globalTransforms.resize(numNodes); for (int nodeIndex : sortedNodes) { auto& node = _data->nodes[nodeIndex]; joint.parentIndex = parents[nodeIndex]; if (joint.parentIndex != -1) { joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.transform = transforms[nodeIndex].first(); joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); joint.postTransform = glm::scale(glm::mat4(), scale); joint.parentIndex = parents[nodeIndex]; globalTransforms[nodeIndex] = joint.transform; if (joint.parentIndex != -1) { globalTransforms[nodeIndex] = globalTransforms[joint.parentIndex] * globalTransforms[nodeIndex]; joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.name = node.name; joint.isSkeletonJoint = false; hfmModel.joints.push_back(joint); } hfmModel.shapeVertices.resize(hfmModel.joints.size()); // get offset transform from mapping float unitScaleFactor = 1.0f; float offsetScale = mapping.value("scale", 1.0f).toFloat() * unitScaleFactor; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); hfmModel.offset = glm::translate(glm::mat4(), glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat())) * glm::mat4_cast(offsetRotation) * glm::scale(glm::mat4(), glm::vec3(offsetScale, offsetScale, offsetScale)); // Build skeleton std::vector<glm::mat4> jointInverseBindTransforms; std::vector<glm::mat4> globalBindTransforms; jointInverseBindTransforms.resize(numNodes); globalBindTransforms.resize(numNodes); hfmModel.hasSkeletonJoints = _data->skins_count > 0; if (hfmModel.hasSkeletonJoints) { std::vector<std::vector<float>> inverseBindValues; if (!getSkinInverseBindMatrices(inverseBindValues)) { qDebug(modelformat) << "GLTFSerializer::getSkinInverseBindMatrices: wrong matrices accessor type for model: " << _url; hfmModel.loadErrorCount++; return false; } for (int jointIndex = 0; jointIndex < numNodes; ++jointIndex) { int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; for (size_t s = 0; s < _data->skins_count; ++s) { const auto& skin = _data->skins[s]; size_t jointNodeIndex = 0; joint.isSkeletonJoint = findNodeInPointerArray(&_data->nodes[nodeIndex], skin.joints, skin.joints_count, jointNodeIndex); // build inverse bind matrices if (joint.isSkeletonJoint) { size_t matrixIndex = jointNodeIndex; std::vector<float>& value = inverseBindValues[s]; size_t matrixCount = 16 * matrixIndex; if (matrixCount + 15 >= value.size()) { qDebug(modelformat) << "GLTFSerializer::buildGeometry: not enough entries in jointInverseBindTransforms: " << _url; hfmModel.loadErrorCount++; return false; } jointInverseBindTransforms[jointIndex] = glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]); } else { jointInverseBindTransforms[jointIndex] = glm::mat4(); } globalBindTransforms[jointIndex] = jointInverseBindTransforms[jointIndex]; if (joint.parentIndex != -1) { globalBindTransforms[jointIndex] = globalBindTransforms[joint.parentIndex] * globalBindTransforms[jointIndex]; } glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); hfmModel.bindExtents.addPoint(bindTranslation); } hfmModel.joints[jointIndex] = joint; } } // Build materials QVector<QString> materialIDs; QString unknown = "Default"; for (size_t i = 0; i < _data->materials_count; i++) { auto &material = _data->materials[i]; QString mid; if (material.name != nullptr) { mid = QString(material.name); }else{ mid = QString::number(i); } materialIDs.push_back(mid); } for (int i = 0; i < materialIDs.size(); ++i) { QString& matid = materialIDs[i]; hfmModel.materials[matid] = HFMMaterial(); HFMMaterial& hfmMaterial = hfmModel.materials[matid]; hfmMaterial.name = hfmMaterial.materialID = matid; setHFMMaterial(hfmMaterial, _data->materials[i]); } // Build meshes int nodeCount = 0; hfmModel.meshExtents.reset(); for (int nodeIndex : sortedNodes) { auto& node = _data->nodes[nodeIndex]; if (node.mesh != nullptr) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; mesh.modelTransform = globalTransforms[nodeIndex]; 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 root; root.jointIndex = 0; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root); QList<QString> meshAttributes; for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { auto &primitive = node.mesh->primitives[primitiveIndex]; for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { auto &attribute = primitive.attributes[attributeIndex]; QString key(attribute.name); if (!meshAttributes.contains(key)) { meshAttributes.push_back(key); } } } for (size_t primitiveIndex = 0; primitiveIndex < node.mesh->primitives_count; primitiveIndex++) { auto &primitive = node.mesh->primitives[primitiveIndex]; HFMMeshPart part = HFMMeshPart(); if (primitive.indices == nullptr) { qDebug() << "No indices accessor for mesh: " << _url; hfmModel.loadErrorCount++; return false; } auto &indicesAccessor = primitive.indices; // Buffers QVector<int> indices; QVector<float> vertices; int verticesStride = 3; QVector<float> normals; int normalStride = 3; QVector<float> tangents; int tangentStride = 4; QVector<float> texcoords; int texCoordStride = 2; QVector<float> texcoords2; int texCoord2Stride = 2; QVector<float> colors; int colorStride = 3; QVector<uint16_t> joints; int jointStride = 4; QVector<float> weights; int weightStride = 4; indices.resize((int)indicesAccessor->count); size_t readIndicesCount = cgltf_accessor_unpack_indices(indicesAccessor, indices.data(), sizeof(unsigned int), indicesAccessor->count); if (readIndicesCount != indicesAccessor->count) { qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; hfmModel.loadErrorCount++; 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(); // For each vertex (stride is WEIGHTS_PER_VERTEX), it contains index of the cluster that given weight belongs to. QVector<uint16_t> clusterJoints; QVector<float> clusterWeights; for (size_t attributeIndex = 0; attributeIndex < primitive.attributes_count; attributeIndex++) { if (primitive.attributes[attributeIndex].name == nullptr) { qDebug() << "Inalid accessor name for mesh: " << _url; hfmModel.loadErrorCount++; return false; } QString key(primitive.attributes[attributeIndex].name); if (primitive.attributes[attributeIndex].data == nullptr) { qDebug() << "Inalid accessor for mesh: " << _url; hfmModel.loadErrorCount++; return false; } auto accessor = primitive.attributes[attributeIndex].data; int accessorCount = (int)accessor->count; if (key == "POSITION") {