diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 1699722215..a4d18fb111 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -751,107 +751,95 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& } } -void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector& children, int stride, std::vector& result) { - int startingIndex = 0; - int finalIndex = (int)children.size(); - if (stride == -1) { - startingIndex = (int)children.size() - 1; - finalIndex = -1; - } - for (int index = startingIndex; index != finalIndex; index += stride) { - int c = children[index]; - result.push_back(c); - std::vector nested = _file.nodes[c].children.toStdVector(); - if (nested.size() != 0) { - std::sort(nested.begin(), nested.end()); - for (int r : nested) { - if (result.end() == std::find(result.begin(), result.end(), r)) { - getNodeQueueByDepthFirstChildren(nested, stride, result); - } - } - } - } -} - - bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int numNodes = _file.nodes.size(); - //Build dependencies - QVector> nodeDependencies(numNodes); + // Build dependencies + QVector parents; + QVector sortedNodes; + parents.fill(-1, numNodes); + sortedNodes.reserve(numNodes); int nodecount = 0; foreach(auto &node, _file.nodes) { - //nodes_transforms.push_back(getModelTransform(node)); - foreach(int child, node.children) nodeDependencies[child].push_back(nodecount); + foreach(int child, node.children) { + parents[child] = nodecount; + } + sortedNodes.push_back(nodecount); nodecount++; } + + // Build transforms nodecount = 0; foreach(auto &node, _file.nodes) { // collect node transform - _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); - if (nodeDependencies[nodecount].size() == 1) { - int parentidx = nodeDependencies[nodecount][0]; - while (true) { // iterate parents - // collect parents transforms - _file.nodes[nodecount].transforms.push_back(getModelTransform(_file.nodes[parentidx])); - if (nodeDependencies[parentidx].size() == 1) { - parentidx = nodeDependencies[parentidx][0]; - } else break; - } + _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 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++; + } + } + } - // initialize order in which nodes will be parsed - std::vector nodeQueue; - nodeQueue.reserve(numNodes); - int rootNode = 0; - int finalNode = numNodes; - if (!_file.scenes[_file.scene].nodes.contains(0)) { - rootNode = numNodes - 1; - finalNode = -1; - } - bool rootAtStartOfList = rootNode < finalNode; - int nodeListStride = 1; - if (!rootAtStartOfList) { nodeListStride = -1; } - QVector initialSceneNodes = _file.scenes[_file.scene].nodes; - std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); - int sceneRootNode = 0; - int sceneFinalNode = initialSceneNodes.size(); - if (!rootAtStartOfList) { - sceneRootNode = initialSceneNodes.size() - 1; - sceneFinalNode = -1; - } - for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) { - int i = initialSceneNodes[index]; - nodeQueue.push_back(i); - std::vector children = _file.nodes[i].children.toStdVector(); - std::sort(children.begin(), children.end()); - getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue); + // Build map from original to new indices + QVector 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; - hfmModel.hasSkeletonJoints = false; - for (int nodeIndex : nodeQueue) { + for (int nodeIndex : sortedNodes) { auto& node = _file.nodes[nodeIndex]; - joint.parentIndex = -1; - if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) { - joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0])); + joint.parentIndex = parents[nodeIndex]; + if (joint.parentIndex != -1) { + joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.transform = node.transforms.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.postTransform = glm::scale(glm::mat4(), scale); joint.name = node.name; joint.isSkeletonJoint = false; @@ -862,24 +850,25 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build skeleton std::vector jointInverseBindTransforms; jointInverseBindTransforms.resize(numNodes); - if (!_file.skins.isEmpty()) { + hfmModel.hasSkeletonJoints = !_file.skins.isEmpty(); + if (hfmModel.hasSkeletonJoints) { + hfmModel.hasSkeletonJoints = true; std::vector> inverseBindValues; getSkinInverseBindMatrices(inverseBindValues); - int jointIndex = finalNode; - while (jointIndex != rootNode) { - rootAtStartOfList ? jointIndex-- : jointIndex++; - int jOffset = nodeQueue[jointIndex]; + for (int jointIndex = 0; jointIndex < numNodes; jointIndex++) { + int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; - hfmModel.hasSkeletonJoints = true; for (int s = 0; s < _file.skins.size(); s++) { - auto skin = _file.skins[s]; - joint.isSkeletonJoint = skin.joints.contains(jOffset); + const auto& skin = _file.skins[s]; + int matrixIndex = skin.joints.indexOf(nodeIndex); + joint.isSkeletonJoint = skin.joints.contains(nodeIndex); + // build inverse bind matrices if (joint.isSkeletonJoint) { - std::vector value = inverseBindValues[s]; - int matrixCount = 16 * skin.joints.indexOf(jOffset); + std::vector& value = inverseBindValues[s]; + int matrixCount = 16 * matrixIndex; 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], @@ -896,7 +885,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } - //Build materials + // Build materials QVector materialIDs; QString unknown = "Default"; int ukcount = 0; @@ -916,7 +905,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build meshes nodecount = 0; - for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) { + for (int nodeIndex : sortedNodes) { auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { @@ -924,14 +913,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { foreach(auto &primitive, _file.meshes[node.mesh].primitives) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; - if (!hfmModel.hasSkeletonJoints) { + if (!hfmModel.hasSkeletonJoints) { HFMCluster cluster; cluster.jointIndex = nodecount; cluster.inverseBindMatrix = glm::mat4(); cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); - } else { - for (int j = rootNode; j != finalNode; j += nodeListStride) { + } else { // skinned model + for (int j = 0; j < numNodes; j++) { HFMCluster cluster; cluster.jointIndex = j; cluster.inverseBindMatrix = jointInverseBindTransforms[j]; @@ -940,10 +929,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } } HFMCluster root; - root.jointIndex = rootNode; - if (root.jointIndex == -1) { - root.jointIndex = 0; - } + root.jointIndex = 0; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root); @@ -1043,6 +1029,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { 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; @@ -1111,7 +1098,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } } - // adapted from FBXSerializer.cpp + // Build weights (adapted from FBXSerializer.cpp) if (hfmModel.hasSkeletonJoints) { int numClusterIndices = clusterJoints.size(); const int WEIGHTS_PER_VERTEX = 4; @@ -1121,7 +1108,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { mesh.clusterWeights.fill(0, numClusterIndices); for (int c = 0; c < clusterJoints.size(); c++) { - mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]]; + mesh.clusterIndices[c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; } // normalize and compress to 16-bits @@ -1160,12 +1147,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { mesh.meshIndex = hfmModel.meshes.size(); } - } nodecount++; } - return true; } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index d9c477bd99..a9e7152e5b 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -713,7 +713,6 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); - void getNodeQueueByDepthFirstChildren(std::vector& children, int stride, std::vector& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data);