From b7e1798d1bf9a50003c095ee70180b23279c5e91 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Mar 2019 11:28:30 -0700 Subject: [PATCH 1/3] better handling of unrigged vertices on skinned mesh --- libraries/fbx/src/FBXSerializer.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 5246242a1e..ca3659636f 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -1043,7 +1043,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr cluster.transformLink = createMat4(values); } } - clusters.insert(getID(object.properties), cluster); + + // skip empty clusters + if (cluster.indices.size() > 0 && cluster.weights.size() > 0) { + clusters.insert(getID(object.properties), cluster); + } } else if (object.properties.last() == "BlendShapeChannel") { QByteArray name = object.properties.at(1).toByteArray(); @@ -1510,19 +1514,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); int jointIndex = hfmCluster.jointIndex; HFMJoint& joint = hfmModel.joints[jointIndex]; - glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; - glm::vec3 boneEnd = extractTranslation(transformJointToMesh); - glm::vec3 boneBegin = boneEnd; - glm::vec3 boneDirection; - float boneLength = 0.0f; - if (joint.parentIndex != -1) { - boneBegin = extractTranslation(inverseModelTransform * hfmModel.joints[joint.parentIndex].bindTransform); - boneDirection = boneEnd - boneBegin; - boneLength = glm::length(boneDirection); - if (boneLength > EPSILON) { - boneDirection /= boneLength; - } - } glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; ShapeVertices& points = shapeVertices.at(jointIndex); @@ -1575,16 +1566,19 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr int j = i * WEIGHTS_PER_VERTEX; // normalize weights into uint16_t - float totalWeight = weightAccumulators[j]; - for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) { + float totalWeight = 0.0f; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { totalWeight += weightAccumulators[k]; } + + const float ALMOST_HALF = 0.499f; if (totalWeight > 0.0f) { - const float ALMOST_HALF = 0.499f; float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); } + } else { + extracted.mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } } } else { From b24b7fed3d96038aa5c55b2463534868935e1737 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Mar 2019 15:34:41 -0700 Subject: [PATCH 2/3] the root node isn't the first onegit add ../.git add ../. --- libraries/fbx/src/FBXSerializer.cpp | 12 ++++++------ libraries/render-utils/src/CauterizedModel.cpp | 8 ++++---- libraries/render-utils/src/Model.cpp | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index ca3659636f..52f4189bdb 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -1486,8 +1486,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } } - // if we don't have a skinned joint, parent to the model itself - if (extracted.mesh.clusters.isEmpty()) { + // the last cluster is the root cluster + { HFMCluster cluster; cluster.jointIndex = modelIDs.indexOf(modelID); if (cluster.jointIndex == -1) { @@ -1498,13 +1498,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } // whether we're skinned depends on how many clusters are attached - const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); - glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { // this is a multi-mesh joint const int WEIGHTS_PER_VERTEX = 4; int numClusterIndices = extracted.mesh.vertices.size() * WEIGHTS_PER_VERTEX; - extracted.mesh.clusterIndices.fill(0, numClusterIndices); + extracted.mesh.clusterIndices.fill(extracted.mesh.clusters.size() - 1, numClusterIndices); QVector weightAccumulators; weightAccumulators.fill(0.0f, numClusterIndices); @@ -1526,6 +1524,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr int newIndex = it.value(); // remember vertices with at least 1/4 weight + // FIXME: vertices with no weightpainting won't get recorded here const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; if (weight >= EXPANSION_WEIGHT_THRESHOLD) { // transform to joint-frame and save for later @@ -1582,7 +1581,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } } } else { - // this is a single-mesh joint + // this is a single-joint mesh + const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); int jointIndex = firstHFMCluster.jointIndex; HFMJoint& joint = hfmModel.joints[jointIndex]; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index cfb78d6bbc..cfdcec6e99 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -245,7 +245,7 @@ void CauterizedModel::updateRenderItems() { Transform renderTransform = modelTransform; if (useDualQuaternionSkinning) { - if (meshState.clusterDualQuaternions.size() == 1) { + if (meshState.clusterDualQuaternions.size() <= 2) { const auto& dq = meshState.clusterDualQuaternions[0]; Transform transform(dq.getRotation(), dq.getScale(), @@ -253,7 +253,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform.worldTransform(transform); } } else { - if (meshState.clusterMatrices.size() == 1) { + if (meshState.clusterMatrices.size() <= 2) { renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); } } @@ -261,7 +261,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform; if (useDualQuaternionSkinning) { - if (cauterizedMeshState.clusterDualQuaternions.size() == 1) { + if (cauterizedMeshState.clusterDualQuaternions.size() <= 2) { const auto& dq = cauterizedMeshState.clusterDualQuaternions[0]; Transform transform(dq.getRotation(), dq.getScale(), @@ -269,7 +269,7 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform.worldTransform(Transform(transform)); } } else { - if (cauterizedMeshState.clusterMatrices.size() == 1) { + if (cauterizedMeshState.clusterMatrices.size() <= 2) { renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0])); } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a8d3e504f1..3c6565fca9 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -247,7 +247,7 @@ void Model::updateRenderItems() { Transform renderTransform = modelTransform; if (useDualQuaternionSkinning) { - if (meshState.clusterDualQuaternions.size() == 1) { + if (meshState.clusterDualQuaternions.size() <= 2) { const auto& dq = meshState.clusterDualQuaternions[0]; Transform transform(dq.getRotation(), dq.getScale(), @@ -255,7 +255,7 @@ void Model::updateRenderItems() { renderTransform = modelTransform.worldTransform(Transform(transform)); } } else { - if (meshState.clusterMatrices.size() == 1) { + if (meshState.clusterMatrices.size() <= 2) { renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0])); } } From 2b32b77bed0875cb72c98948f82fcc7aa2513b37 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Mar 2019 17:32:40 -0700 Subject: [PATCH 3/3] handle case when clusterMatrices.size() == 0 --- .vs/slnx.sqlite | Bin 0 -> 77824 bytes libraries/render-utils/src/CauterizedModel.cpp | 8 ++++---- libraries/render-utils/src/Model.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 .vs/slnx.sqlite diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..4227190a376249e4edb90c9be12165dbacd46945 GIT binary patch literal 77824 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