From b90660839ece36b2d39f153661489945779739b9 Mon Sep 17 00:00:00 2001 From: John Hood III Date: Fri, 24 Mar 2023 14:23:31 -0700 Subject: [PATCH 1/3] Check num vertices vs weights count --- lib/mayaUsd/fileio/utils/jointWriteUtils.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp index b52ba6358e..733d93e9c3 100644 --- a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp +++ b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp @@ -237,6 +237,16 @@ int UsdMayaJointUtil::getCompressedSkinWeights( unsigned int numInfluences; skinCluster.getWeights(outputDagPath, components.object(), weights, numInfluences); + if(weights.length() < numVertices * numInfluences) { + MString msg("Number of weights ("); + msg += weights.length(); + msg += ") exceeds number of influences * number of vertices ("; + msg += numInfluences * numVertices; + msg += ") - skin binding will not be written"; + MGlobal::displayError(msg); + throw std::runtime_error(msg.asChar()); + } + // Determine how many influence/weight "slots" we actually need per point. // For example, if there are the joints /a, /a/b, and /a/c, but each point // only has non-zero weighting for a single joint, then we only need one From b2198fb435523e675121233268127b6a19b2a48b Mon Sep 17 00:00:00 2001 From: John Hood III Date: Fri, 24 Mar 2023 14:27:48 -0700 Subject: [PATCH 2/3] clang format --- lib/mayaUsd/fileio/utils/jointWriteUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp index 733d93e9c3..5d71bf747e 100644 --- a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp +++ b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp @@ -237,7 +237,7 @@ int UsdMayaJointUtil::getCompressedSkinWeights( unsigned int numInfluences; skinCluster.getWeights(outputDagPath, components.object(), weights, numInfluences); - if(weights.length() < numVertices * numInfluences) { + if (weights.length() < numVertices * numInfluences) { MString msg("Number of weights ("); msg += weights.length(); msg += ") exceeds number of influences * number of vertices ("; From df83033faae88f9da1466396761e08740044c335 Mon Sep 17 00:00:00 2001 From: John Hood III Date: Mon, 27 Mar 2023 13:08:35 -0700 Subject: [PATCH 3/3] Expanded msg, added unittest --- lib/mayaUsd/fileio/utils/jointWriteUtils.cpp | 23 +++++-- test/lib/usd/translators/CMakeLists.txt | 1 + test/lib/usd/translators/testUsdExportSkin.py | 61 +++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/lib/usd/translators/testUsdExportSkin.py diff --git a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp index 5d71bf747e..68d89697e3 100644 --- a/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp +++ b/lib/mayaUsd/fileio/utils/jointWriteUtils.cpp @@ -237,12 +237,23 @@ int UsdMayaJointUtil::getCompressedSkinWeights( unsigned int numInfluences; skinCluster.getWeights(outputDagPath, components.object(), weights, numInfluences); + if (numInfluences <= 0) { + MString msg("No influences found for skinCluster "); + msg += skinCluster.name(); + MGlobal::displayError(msg); + throw std::runtime_error(msg.asChar()); + } + if (weights.length() < numVertices * numInfluences) { - MString msg("Number of weights ("); - msg += weights.length(); - msg += ") exceeds number of influences * number of vertices ("; - msg += numInfluences * numVertices; - msg += ") - skin binding will not be written"; + MString msg("The number of vertices on the exported mesh "); + msg += outputDagPath.partialPathName(); + msg += "("; + msg += numVertices; + + msg += ") do not match the number of vertices where the skinCluster was applied ("; + msg += weights.length() / numInfluences; + msg += "). Remove any nodes that change mesh topology after the skinCluster."; + MGlobal::displayError(msg); throw std::runtime_error(msg.asChar()); } @@ -473,4 +484,4 @@ MObject UsdMayaJointUtil::writeSkinningData( return inMeshObj; } -PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/test/lib/usd/translators/CMakeLists.txt b/test/lib/usd/translators/CMakeLists.txt index f25a311a14..fc762f001e 100644 --- a/test/lib/usd/translators/CMakeLists.txt +++ b/test/lib/usd/translators/CMakeLists.txt @@ -39,6 +39,7 @@ set(TEST_SCRIPT_FILES testUsdExportSelection.py testUsdExportSelectionHierarchy.py testUsdExportSkeleton.py + testUsdExportSkin.py testUsdExportStripNamespaces.py testUsdExportStroke.py testUsdExportUserTaggedAttributes.py diff --git a/test/lib/usd/translators/testUsdExportSkin.py b/test/lib/usd/translators/testUsdExportSkin.py new file mode 100644 index 0000000000..a409b0b1a1 --- /dev/null +++ b/test/lib/usd/translators/testUsdExportSkin.py @@ -0,0 +1,61 @@ +#!/usr/bin/env mayapy +# +# Copyright 2023 Apple Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import os +import unittest + +from maya import cmds +from maya import standalone +from maya.api import OpenMaya as OM + +from pxr import Gf, Sdf, Tf, Usd, UsdGeom, UsdSkel, UsdUtils, Vt + +import fixturesUtils + + +def build_scene(): + """Set up simple skinCluster, add a polyReduce after""" + cmds.file(f=1, new=1) + root = cmds.joint() + cmds.joint() + sphere = cmds.polySphere() + cmds.select(root, add=1) + group = cmds.group() + + cmds.skinCluster(root, sphere[0]) + cmds.select(sphere[0]) + + cmds.polyReduce(p=50) + return group + +class TestUsdExportSkin(unittest.TestCase): + @classmethod + def setUpClass(cls): + inputPath = fixturesUtils.setUpClass(__file__) + # cls.outFile = os.path.join + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def test_exportSkinDifferingTopology(self): + """Raise a runtime error if the topology has changed downstream from the skinCluster""" + grp = build_scene() + self.assertRaises(RuntimeError, cmds.mayaUSDExport, file="Does_not_export.usdc", skn="auto", skl="auto") + + +if __name__ == '__main__': + unittest.main(verbosity=2)