From d1d999dd7228d186ceea2165c73f68fc2b068edd Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Fri, 5 Apr 2019 19:58:26 +0100 Subject: [PATCH 1/6] init node order fix --- libraries/fbx/src/GLTFSerializer.cpp | 37 ++++++++++++++++++++-------- libraries/fbx/src/GLTFSerializer.h | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 1699722215..ec58f15a95 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -751,7 +751,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& } } -void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result) { +void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result) { int startingIndex = 0; int finalIndex = (int)children.size(); if (stride == -1) { @@ -763,10 +763,12 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children result.push_back(c); std::vector<int> nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { - std::sort(nested.begin(), nested.end()); + if (order) { + std::sort(nested.begin(), nested.end()); + } for (int r : nested) { if (result.end() == std::find(result.begin(), result.end(), r)) { - getNodeQueueByDepthFirstChildren(nested, stride, result); + getNodeQueueByDepthFirstChildren(nested, stride, order, result); } } } @@ -776,13 +778,18 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int numNodes = _file.nodes.size(); + bool skinnedModel = !_file.skins.isEmpty(); //Build dependencies QVector<QVector<int>> nodeDependencies(numNodes); int nodecount = 0; + bool parentOutOfOrder = false; 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) { + nodeDependencies[child].push_back(nodecount); + parentOutOfOrder |= nodecount < child; + } nodecount++; } @@ -806,20 +813,27 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // initialize order in which nodes will be parsed + bool sortNodes = (!parentOutOfOrder && skinnedModel) || !skinnedModel; std::vector<int> nodeQueue; nodeQueue.reserve(numNodes); int rootNode = 0; int finalNode = numNodes; - if (!_file.scenes[_file.scene].nodes.contains(0)) { - rootNode = numNodes - 1; - finalNode = -1; + for (int sceneNode : _file.scenes[_file.scene].nodes) { + if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) { + rootNode = numNodes - 1; + finalNode = -1; + break; + } } bool rootAtStartOfList = rootNode < finalNode; int nodeListStride = 1; if (!rootAtStartOfList) { nodeListStride = -1; } QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; - std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); + if (sortNodes) { + std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); + } + int sceneRootNode = 0; int sceneFinalNode = initialSceneNodes.size(); if (!rootAtStartOfList) { @@ -830,10 +844,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int i = initialSceneNodes[index]; nodeQueue.push_back(i); std::vector<int> children = _file.nodes[i].children.toStdVector(); - std::sort(children.begin(), children.end()); - getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue); + if (sortNodes) { + std::sort(children.begin(), children.end()); + } + getNodeQueueByDepthFirstChildren(children, nodeListStride, sortNodes, nodeQueue); } + // Build joints HFMJoint joint; joint.distanceToParent = 0; diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index d9c477bd99..dd3649478e 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -713,7 +713,7 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues); - void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result); + void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 40fe156c8b25ec2a3124d6c5f9d3fd02fe46fc0e Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Mon, 8 Apr 2019 19:54:31 +0100 Subject: [PATCH 2/6] naming, condition --- libraries/fbx/src/GLTFSerializer.cpp | 14 +++++++------- libraries/fbx/src/GLTFSerializer.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index ec58f15a95..21df5bc2dc 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -751,7 +751,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& } } -void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result) { +void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result) { int startingIndex = 0; int finalIndex = (int)children.size(); if (stride == -1) { @@ -763,12 +763,12 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children result.push_back(c); std::vector<int> nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { - if (order) { + if (needToSort) { std::sort(nested.begin(), nested.end()); } for (int r : nested) { if (result.end() == std::find(result.begin(), result.end(), r)) { - getNodeQueueByDepthFirstChildren(nested, stride, order, result); + getNodeQueueByDepthFirstChildren(nested, stride, needToSort, result); } } } @@ -813,7 +813,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // initialize order in which nodes will be parsed - bool sortNodes = (!parentOutOfOrder && skinnedModel) || !skinnedModel; + bool needToSort = !parentOutOfOrder || !skinnedModel; std::vector<int> nodeQueue; nodeQueue.reserve(numNodes); int rootNode = 0; @@ -830,7 +830,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { if (!rootAtStartOfList) { nodeListStride = -1; } QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; - if (sortNodes) { + if (needToSort) { std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); } @@ -844,10 +844,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int i = initialSceneNodes[index]; nodeQueue.push_back(i); std::vector<int> children = _file.nodes[i].children.toStdVector(); - if (sortNodes) { + if (needToSort) { std::sort(children.begin(), children.end()); } - getNodeQueueByDepthFirstChildren(children, nodeListStride, sortNodes, nodeQueue); + getNodeQueueByDepthFirstChildren(children, nodeListStride, needToSort, nodeQueue); } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index dd3649478e..3eee24284e 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -713,7 +713,7 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues); - void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result); + void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From c970a58715fcc4e568b2dd99acf21939ede919ce Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Tue, 9 Apr 2019 17:29:07 +0100 Subject: [PATCH 3/6] comments, edge case for skinned --- libraries/fbx/src/GLTFSerializer.cpp | 31 ++++++++++++++-------------- libraries/fbx/src/GLTFSerializer.h | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 21df5bc2dc..e4a759fa9b 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -751,24 +751,23 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& } } -void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result) { +void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result) { int startingIndex = 0; int finalIndex = (int)children.size(); - if (stride == -1) { + if (stride == -1 || addChildrenInReverseOrder) { startingIndex = (int)children.size() - 1; finalIndex = -1; + stride = -1; } for (int index = startingIndex; index != finalIndex; index += stride) { int c = children[index]; result.push_back(c); std::vector<int> nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { - if (needToSort) { - std::sort(nested.begin(), nested.end()); - } + std::sort(nested.begin(), nested.end()); for (int r : nested) { if (result.end() == std::find(result.begin(), result.end(), r)) { - getNodeQueueByDepthFirstChildren(nested, stride, needToSort, result); + getNodeQueueByDepthFirstChildren(nested, stride, addChildrenInReverseOrder, result); } } } @@ -783,12 +782,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { //Build dependencies QVector<QVector<int>> nodeDependencies(numNodes); int nodecount = 0; - bool parentOutOfOrder = false; + bool parentGreaterThanChild = false; foreach(auto &node, _file.nodes) { //nodes_transforms.push_back(getModelTransform(node)); foreach(int child, node.children) { nodeDependencies[child].push_back(nodecount); - parentOutOfOrder |= nodecount < child; + parentGreaterThanChild |= nodecount > child; } nodecount++; } @@ -813,12 +812,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // initialize order in which nodes will be parsed - bool needToSort = !parentOutOfOrder || !skinnedModel; std::vector<int> nodeQueue; nodeQueue.reserve(numNodes); int rootNode = 0; int finalNode = numNodes; for (int sceneNode : _file.scenes[_file.scene].nodes) { + // reverse the order in which the nodes are initialized if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) { rootNode = numNodes - 1; finalNode = -1; @@ -830,9 +829,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { if (!rootAtStartOfList) { nodeListStride = -1; } QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; - if (needToSort) { - std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); - } + std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); int sceneRootNode = 0; int sceneFinalNode = initialSceneNodes.size(); @@ -840,14 +837,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { sceneRootNode = initialSceneNodes.size() - 1; sceneFinalNode = -1; } + // this is an edge case where, for a skinned model, there is a parent who's index is greater than it's child's index + // when the opposite is expected + // in this case, we want the order of the children to be reversed, even if the root node is 0 + bool addChildrenInReverseOrder = rootAtStartOfList && parentGreaterThanChild && skinnedModel; for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) { int i = initialSceneNodes[index]; nodeQueue.push_back(i); std::vector<int> children = _file.nodes[i].children.toStdVector(); - if (needToSort) { - std::sort(children.begin(), children.end()); - } - getNodeQueueByDepthFirstChildren(children, nodeListStride, needToSort, nodeQueue); + std::sort(children.begin(), children.end()); + getNodeQueueByDepthFirstChildren(children, nodeListStride, addChildrenInReverseOrder, nodeQueue); } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index 3eee24284e..ca4e79fcb6 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -713,7 +713,7 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues); - void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result); + void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 3c84a46cba8590980e5ea3479465705b72de5327 Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Mon, 15 Apr 2019 20:27:54 +0100 Subject: [PATCH 4/6] update how nodes are initially parsed --- libraries/fbx/src/GLTFSerializer.cpp | 143 +++++++++++++-------------- libraries/fbx/src/GLTFSerializer.h | 1 - 2 files changed, 69 insertions(+), 75 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index e4a759fa9b..95d055481a 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -751,43 +751,25 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& } } -void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result) { - int startingIndex = 0; - int finalIndex = (int)children.size(); - if (stride == -1 || addChildrenInReverseOrder) { - startingIndex = (int)children.size() - 1; - finalIndex = -1; - stride = -1; - } - for (int index = startingIndex; index != finalIndex; index += stride) { - int c = children[index]; - result.push_back(c); - std::vector<int> 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, addChildrenInReverseOrder, result); - } - } - } - } -} - - bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int numNodes = _file.nodes.size(); bool skinnedModel = !_file.skins.isEmpty(); + //Build dependencies QVector<QVector<int>> nodeDependencies(numNodes); + QVector<int> parents; + QVector<int> nodesToSort = _file.scenes[_file.scene].nodes; + parents.fill(-1, numNodes); + nodesToSort.reserve(numNodes); int nodecount = 0; - bool parentGreaterThanChild = false; foreach(auto &node, _file.nodes) { - //nodes_transforms.push_back(getModelTransform(node)); foreach(int child, node.children) { nodeDependencies[child].push_back(nodecount); - parentGreaterThanChild |= nodecount > child; + parents[child] = nodecount; + } + if (!nodesToSort.contains(nodecount)) { + nodesToSort.push_back(nodecount); } nodecount++; } @@ -806,47 +788,62 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } else break; } } - nodecount++; } - + // initialize order in which nodes will be parsed std::vector<int> nodeQueue; - nodeQueue.reserve(numNodes); - int rootNode = 0; - int finalNode = numNodes; - for (int sceneNode : _file.scenes[_file.scene].nodes) { - // reverse the order in which the nodes are initialized - if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) { - rootNode = numNodes - 1; - finalNode = -1; - break; + QVector<int> originalToNewNodeIndexMap; + QVector<bool> hasBeenSorted; + originalToNewNodeIndexMap.fill(-1, numNodes); + hasBeenSorted.fill(false, numNodes); + nodeQueue = _file.scenes[_file.scene].nodes.toStdVector(); + + for (int sceneNodeCount = 0; sceneNodeCount < _file.scenes[_file.scene].nodes.size(); sceneNodeCount++) { + int sceneNode = nodeQueue[sceneNodeCount]; + originalToNewNodeIndexMap[sceneNode] = sceneNodeCount; + nodesToSort[nodesToSort.indexOf(sceneNode)] = nodesToSort.back(); + nodesToSort.pop_back(); + hasBeenSorted[sceneNode] = true; + for (int child : _file.nodes[sceneNode].children.toStdVector()) { + nodesToSort[nodesToSort.indexOf(child)] = nodesToSort.back(); + nodesToSort.pop_back(); } - } - bool rootAtStartOfList = rootNode < finalNode; - int nodeListStride = 1; - if (!rootAtStartOfList) { nodeListStride = -1; } - QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; - std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); + for (int child : _file.nodes[sceneNode].children) { + originalToNewNodeIndexMap[child] = nodeQueue.size(); + nodeQueue.push_back(child); + hasBeenSorted[child] = true; - int sceneRootNode = 0; - int sceneFinalNode = initialSceneNodes.size(); - if (!rootAtStartOfList) { - sceneRootNode = initialSceneNodes.size() - 1; - sceneFinalNode = -1; - } - // this is an edge case where, for a skinned model, there is a parent who's index is greater than it's child's index - // when the opposite is expected - // in this case, we want the order of the children to be reversed, even if the root node is 0 - bool addChildrenInReverseOrder = rootAtStartOfList && parentGreaterThanChild && skinnedModel; - for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) { - int i = initialSceneNodes[index]; - nodeQueue.push_back(i); - std::vector<int> children = _file.nodes[i].children.toStdVector(); - std::sort(children.begin(), children.end()); - getNodeQueueByDepthFirstChildren(children, nodeListStride, addChildrenInReverseOrder, nodeQueue); + if (!_file.nodes[child].children.isEmpty() && nodeQueue.size() < numNodes) { + int newSize = nodesToSort.size(); + while (!nodesToSort.isEmpty()) { + int i = 0; + + while (i < nodesToSort.size()) { + int nodeIndex = nodesToSort[i]; + int parentIndex = parents[nodeIndex]; + newSize = nodesToSort.size(); + + if ((parentIndex == -1 || hasBeenSorted[parentIndex])) { + originalToNewNodeIndexMap[nodeIndex] = nodeQueue.size(); + nodeQueue.push_back(nodeIndex); + hasBeenSorted[nodeIndex] = true; + // copy back and pop + nodesToSort[i] = nodesToSort.back(); + nodesToSort.pop_back(); + } else { // skip + i++; + } + } + // if the end of nodesToSort is reached without removing any nodes, break to move onto the next child + if (newSize == nodesToSort.size() && i == nodesToSort.size()) { + break; + } + } + } + } } @@ -861,13 +858,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { 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 = originalToNewNodeIndexMap[parents[nodeIndex]]; } 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; @@ -882,20 +879,19 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { std::vector<std::vector<float>> 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 = nodeQueue[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); if (joint.isSkeletonJoint) { - std::vector<float> value = inverseBindValues[s]; - int matrixCount = 16 * skin.joints.indexOf(jOffset); + std::vector<float>& 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], @@ -932,7 +928,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 : nodeQueue) { auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { @@ -947,7 +943,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); } else { - for (int j = rootNode; j != finalNode; j += nodeListStride) { + for (int j : nodeQueue) { HFMCluster cluster; cluster.jointIndex = j; cluster.inverseBindMatrix = jointInverseBindTransforms[j]; @@ -956,7 +952,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } } HFMCluster root; - root.jointIndex = rootNode; + root.jointIndex = nodeQueue[0]; if (root.jointIndex == -1) { root.jointIndex = 0; } @@ -1181,7 +1177,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { nodecount++; } - return true; } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index ca4e79fcb6..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<std::vector<float>>& inverseBindMatrixValues); - void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 104ed758b9f4fa32ca232e1247284af474ed8eb6 Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Wed, 17 Apr 2019 17:52:48 +0100 Subject: [PATCH 5/6] re-order nodes, cluster update --- libraries/fbx/src/GLTFSerializer.cpp | 140 +++++++++++---------------- 1 file changed, 57 insertions(+), 83 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 95d055481a..497d1f6ef0 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -753,107 +753,82 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int numNodes = _file.nodes.size(); - bool skinnedModel = !_file.skins.isEmpty(); - - //Build dependencies - QVector<QVector<int>> nodeDependencies(numNodes); + // Build dependencies QVector<int> parents; - QVector<int> nodesToSort = _file.scenes[_file.scene].nodes; + QVector<int> sortedNodes; parents.fill(-1, numNodes); - nodesToSort.reserve(numNodes); + sortedNodes.reserve(numNodes); int nodecount = 0; foreach(auto &node, _file.nodes) { foreach(int child, node.children) { - nodeDependencies[child].push_back(nodecount); parents[child] = nodecount; } - if (!nodesToSort.contains(nodecount)) { - nodesToSort.push_back(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++; } - // initialize order in which nodes will be parsed - std::vector<int> nodeQueue; - QVector<int> originalToNewNodeIndexMap; + // 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; - originalToNewNodeIndexMap.fill(-1, numNodes); hasBeenSorted.fill(false, numNodes); - nodeQueue = _file.scenes[_file.scene].nodes.toStdVector(); - - for (int sceneNodeCount = 0; sceneNodeCount < _file.scenes[_file.scene].nodes.size(); sceneNodeCount++) { - int sceneNode = nodeQueue[sceneNodeCount]; - originalToNewNodeIndexMap[sceneNode] = sceneNodeCount; - nodesToSort[nodesToSort.indexOf(sceneNode)] = nodesToSort.back(); - nodesToSort.pop_back(); - hasBeenSorted[sceneNode] = true; - for (int child : _file.nodes[sceneNode].children.toStdVector()) { - nodesToSort[nodesToSort.indexOf(child)] = nodesToSort.back(); - nodesToSort.pop_back(); - } - - for (int child : _file.nodes[sceneNode].children) { - originalToNewNodeIndexMap[child] = nodeQueue.size(); - nodeQueue.push_back(child); - hasBeenSorted[child] = true; - - if (!_file.nodes[child].children.isEmpty() && nodeQueue.size() < numNodes) { - int newSize = nodesToSort.size(); - while (!nodesToSort.isEmpty()) { - int i = 0; - - while (i < nodesToSort.size()) { - int nodeIndex = nodesToSort[i]; - int parentIndex = parents[nodeIndex]; - newSize = nodesToSort.size(); - - if ((parentIndex == -1 || hasBeenSorted[parentIndex])) { - originalToNewNodeIndexMap[nodeIndex] = nodeQueue.size(); - nodeQueue.push_back(nodeIndex); - hasBeenSorted[nodeIndex] = true; - // copy back and pop - nodesToSort[i] = nodesToSort.back(); - nodesToSort.pop_back(); - } else { // skip - i++; - } - } - // if the end of nodesToSort is reached without removing any nodes, break to move onto the next child - if (newSize == nodesToSort.size() && i == nodesToSort.size()) { - break; - } + 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++; + break; } + 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; - hfmModel.hasSkeletonJoints = false; - for (int nodeIndex : nodeQueue) { + for (int nodeIndex : sortedNodes) { auto& node = _file.nodes[nodeIndex]; joint.parentIndex = -1; @@ -873,22 +848,24 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build skeleton + hfmModel.hasSkeletonJoints = false; std::vector<glm::mat4> jointInverseBindTransforms; jointInverseBindTransforms.resize(numNodes); if (!_file.skins.isEmpty()) { + hfmModel.hasSkeletonJoints = true; std::vector<std::vector<float>> inverseBindValues; getSkinInverseBindMatrices(inverseBindValues); for (int jointIndex = 0; jointIndex < numNodes; jointIndex++) { - int nodeIndex = nodeQueue[jointIndex]; + int nodeIndex = sortedNodes[jointIndex]; auto joint = hfmModel.joints[jointIndex]; - hfmModel.hasSkeletonJoints = true; for (int s = 0; s < _file.skins.size(); s++) { 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<float>& value = inverseBindValues[s]; int matrixCount = 16 * matrixIndex; @@ -908,7 +885,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } - //Build materials + // Build materials QVector<QString> materialIDs; QString unknown = "Default"; int ukcount = 0; @@ -928,7 +905,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build meshes nodecount = 0; - for (int nodeIndex : nodeQueue) { + for (int nodeIndex : sortedNodes) { auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { @@ -936,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 : nodeQueue) { + } else { // skinned model + for (int j = 0; j < numNodes; j++) { HFMCluster cluster; cluster.jointIndex = j; cluster.inverseBindMatrix = jointInverseBindTransforms[j]; @@ -952,10 +929,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } } HFMCluster root; - root.jointIndex = nodeQueue[0]; - if (root.jointIndex == -1) { - root.jointIndex = 0; - } + root.jointIndex = sortedNodes[0]; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root); @@ -1055,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; @@ -1123,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; @@ -1133,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 @@ -1172,7 +1147,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { mesh.meshIndex = hfmModel.meshes.size(); } - } nodecount++; } From 2fab5a0945cdce019171495200b580751fa55d8a Mon Sep 17 00:00:00 2001 From: raveenajain <jain.rav@husky.neu.edu> Date: Wed, 17 Apr 2019 21:14:44 +0100 Subject: [PATCH 6/6] parents, skeleton, break fix --- libraries/fbx/src/GLTFSerializer.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 497d1f6ef0..a4d18fb111 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -807,7 +807,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { sortedNodes[i] = nextNode; sortedNodes[j] = currentNode; i++; - break; + currentNode = sortedNodes[i]; } j++; } @@ -831,9 +831,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { for (int nodeIndex : sortedNodes) { auto& node = _file.nodes[nodeIndex]; - joint.parentIndex = -1; - if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) { - joint.parentIndex = originalToNewNodeIndexMap[parents[nodeIndex]]; + joint.parentIndex = parents[nodeIndex]; + if (joint.parentIndex != -1) { + joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex]; } joint.transform = node.transforms.first(); joint.translation = extractTranslation(joint.transform); @@ -848,10 +848,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build skeleton - hfmModel.hasSkeletonJoints = false; std::vector<glm::mat4> jointInverseBindTransforms; jointInverseBindTransforms.resize(numNodes); - if (!_file.skins.isEmpty()) { + hfmModel.hasSkeletonJoints = !_file.skins.isEmpty(); + if (hfmModel.hasSkeletonJoints) { hfmModel.hasSkeletonJoints = true; std::vector<std::vector<float>> inverseBindValues; getSkinInverseBindMatrices(inverseBindValues); @@ -929,7 +929,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } } HFMCluster root; - root.jointIndex = sortedNodes[0]; + root.jointIndex = 0; root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root);