From de1ea72620d0abbe0568c50759c8a9f329d46100 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 5 Dec 2023 09:37:21 -0500 Subject: [PATCH] EMSUSD-126 fix finding strongest layer. - Fix getStrongestLayer to find layers relative to their parent layer. - Fix the reporting of unknown layers with a clearer message. - Add a unit test for switching a variant in a sub-layer. - Add a unit test for switching a variant in a sub-layer when the variant is in a reference. --- lib/usdUfe/ufe/Utils.cpp | 8 +- lib/usdUfe/utils/layers.cpp | 16 +- test/lib/ufe/CMakeLists.txt | 1 + test/lib/ufe/testVariant.py | 170 ++++++++++++++++++ .../variantInRef/layout/assets/asset.usda | 8 + .../layout/assets/geo_variants.usda | 59 ++++++ .../variantInRef/layout/layout.usda | 7 + test/testSamples/variantInRef/shot.usda | 8 + 8 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 test/lib/ufe/testVariant.py create mode 100644 test/testSamples/variantInRef/layout/assets/asset.usda create mode 100644 test/testSamples/variantInRef/layout/assets/geo_variants.usda create mode 100644 test/testSamples/variantInRef/layout/layout.usda create mode 100644 test/testSamples/variantInRef/shot.usda diff --git a/lib/usdUfe/ufe/Utils.cpp b/lib/usdUfe/ufe/Utils.cpp index 2553d1e555..d74e7028f9 100644 --- a/lib/usdUfe/ufe/Utils.cpp +++ b/lib/usdUfe/ufe/Utils.cpp @@ -689,10 +689,16 @@ bool isPropertyMetadataEditAllowed( = UsdUfe::getStrongerLayer(stage, targetLayer, topAuthoredLayer, true); bool allowed = (strongestLayer == targetLayer); if (!allowed && errMsg) { + std::string strongName; + if (strongestLayer) + strongName = strongestLayer->GetDisplayName(); + else + strongName = "a layer we could not identify"; + *errMsg = TfStringPrintf( "Cannot edit [%s] attribute because there is a stronger opinion in [%s].", metadataName.GetText(), - strongestLayer ? strongestLayer->GetDisplayName().c_str() : "some layer"); + strongName.c_str()); } return allowed; } diff --git a/lib/usdUfe/utils/layers.cpp b/lib/usdUfe/utils/layers.cpp index bde07c9ce6..5b553b4aba 100644 --- a/lib/usdUfe/utils/layers.cpp +++ b/lib/usdUfe/utils/layers.cpp @@ -197,13 +197,15 @@ SdfLayerHandle getStrongerLayer( return layer2; for (auto path : root->GetSubLayerPaths()) { - SdfLayerRefPtr subLayer = SdfLayer::FindOrOpen(path); - if (subLayer) { - SdfLayerHandle stronger = getStrongerLayer(subLayer, layer1, layer2); - if (!stronger.IsInvalid()) { - return stronger; - } - } + SdfLayerRefPtr subLayer = SdfLayer::FindRelativeToLayer(root, path); + if (!subLayer) + continue; + + SdfLayerHandle stronger = getStrongerLayer(subLayer, layer1, layer2); + if (!stronger) + continue; + + return stronger; } return SdfLayerHandle(); diff --git a/test/lib/ufe/CMakeLists.txt b/test/lib/ufe/CMakeLists.txt index b81f66fab2..229f25c3bc 100644 --- a/test/lib/ufe/CMakeLists.txt +++ b/test/lib/ufe/CMakeLists.txt @@ -38,6 +38,7 @@ set(TEST_SCRIPT_FILES testTransform3dTranslate.py testUIInfoHandler.py testUfePythonImport.py + testVariant.py testVisibilityCmd.py ) diff --git a/test/lib/ufe/testVariant.py b/test/lib/ufe/testVariant.py new file mode 100644 index 0000000000..40587b7879 --- /dev/null +++ b/test/lib/ufe/testVariant.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python + +# +# Copyright 2023 Autodesk +# +# 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 mayaUsd +import fixturesUtils +import mayaUtils +import usdUtils +import testUtils + +from maya import cmds +from maya import standalone + +import ufe + +from pxr import Usd, Sdf + +import unittest + + +class VariantTestCase(unittest.TestCase): + ''' + Verify variants on a USD scene. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False) + + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + @classmethod + def tearDownClass(cls): + cmds.file(new=True, force=True) + + standalone.uninitialize() + + def setUp(self): + # Load plugins + cmds.file(new=True, force=True) + self.assertTrue(self.pluginsLoaded) + + def verifyChildren(self, item, expectedChildrenPaths): + ''' + Verify that the list of children UFE paths really are the children + of the given UFE item. + ''' + itemHierarchy = ufe.Hierarchy.hierarchy(item) + itemChildren = itemHierarchy.children() + self.assertEqual([ufe.PathString.string(child.path()) for child in itemChildren], expectedChildrenPaths) + + def testSwitchVariantMultiLayers(self): + ''' + Switch variants in a layer above a layer that already has a variant selection. + ''' + import mayaUsd_createStageWithNewLayer + import maya.internal.ufeSupport.ufeCmdWrapper as ufeCmd + + # Create a scene with three layers. + proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() + rootLayer = stage.GetRootLayer() + subLayers = usdUtils.addNewLayersToLayer(rootLayer, 3) + + # Create two variants in the deepest layer. + with Usd.EditContext(stage, subLayers[-1]): + xformPrim = stage.DefinePrim('/Xform1', 'Xform') + variantSet = xformPrim.GetVariantSets().AddVariantSet('modelingVariant') + variantSet.AddVariant('cube') + variantSet.AddVariant('sphere') + variantSet.SetVariantSelection('cube') + with variantSet.GetVariantEditContext(): + stage.DefinePrim('/Xform1/Cube', 'Cube') + variantSet.SetVariantSelection('sphere') + with variantSet.GetVariantEditContext(): + stage.DefinePrim('/Xform1/Sphere', 'Sphere') + + # Paths and item that will be used in the test. + xformPathStr = '%s,/Xform1' % proxyShapePathStr + cubePathStr = '%s/Cube' % xformPathStr + spherePathStr = '%s/Sphere' % xformPathStr + + xformItem = ufe.Hierarchy.createItem(ufe.PathString.path(xformPathStr)) + + # The sphere is the sole child of Xform1. + self.verifyChildren(xformItem, [spherePathStr]) + + # Switch variants using a command: the cube is now the sole child of + # Xform1. + xformCtxOps = ufe.ContextOps.contextOps(xformItem) + cmd = xformCtxOps.doOpCmd(['Variant Sets', 'modelingVariant', 'cube']) + ufeCmd.execute(cmd) + + self.verifyChildren(xformItem, [cubePathStr]) + + # Undo: sphere is back. + cmds.undo() + + self.verifyChildren(xformItem, [spherePathStr]) + + # Redo: cube is back. + cmds.redo() + + self.verifyChildren(xformItem, [cubePathStr]) + + def testSwitchVariantFromReference(self): + ''' + Switch variants declared in a reference. + Make sure the edit restrictions work correctly when the target is a sub-layer + and the top opinion is in the reference and the layers are using relative paths. + ''' + import mayaUsd_createStageWithNewLayer + import maya.internal.ufeSupport.ufeCmdWrapper as ufeCmd + + # Create a scene with three layers and variants in a reference. + shotFileName = testUtils.getTestScene("variantInRef", "shot.usda") + proxyShapePathStr, stage = mayaUtils.createProxyFromFile(shotFileName) + + # Paths and item that will be used in the test. + varyingPathStr = '%s,/assets/varying' % proxyShapePathStr + cubePathStr = '%s/CubePrim' % varyingPathStr + spherePathStr = '%s/SpherePrim' % varyingPathStr + + rootItem = ufe.Hierarchy.createItem(ufe.PathString.path(varyingPathStr)) + + # The sphere is the sole child of Xform1. + self.verifyChildren(rootItem, [spherePathStr]) + + # Switch variants using a command: the cube is now the sole child of + # Xform1. + rootLayer = stage.GetRootLayer() + subIdentifier = rootLayer.subLayerPaths[0] + subLayer = Sdf.Layer.FindRelativeToLayer(rootLayer, subIdentifier) + with Usd.EditContext(stage, subLayer): + xformCtxOps = ufe.ContextOps.contextOps(rootItem) + cmd = xformCtxOps.doOpCmd(['Variant Sets', 'geo', 'cube']) + ufeCmd.execute(cmd) + + self.verifyChildren(rootItem, [cubePathStr]) + + # Undo: sphere is back. + cmds.undo() + + self.verifyChildren(rootItem, [spherePathStr]) + + # Redo: cube is back. + cmds.redo() + + self.verifyChildren(rootItem, [cubePathStr]) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/testSamples/variantInRef/layout/assets/asset.usda b/test/testSamples/variantInRef/layout/assets/asset.usda new file mode 100644 index 0000000000..9ab545fe9d --- /dev/null +++ b/test/testSamples/variantInRef/layout/assets/asset.usda @@ -0,0 +1,8 @@ +#usda 1.0 + +def Scope "assets" ( + prepend references = @geo_variants.usda@ + ) + { + } + diff --git a/test/testSamples/variantInRef/layout/assets/geo_variants.usda b/test/testSamples/variantInRef/layout/assets/geo_variants.usda new file mode 100644 index 0000000000..1357a7c667 --- /dev/null +++ b/test/testSamples/variantInRef/layout/assets/geo_variants.usda @@ -0,0 +1,59 @@ +#usda 1.0 +( + defaultPrim = "root" +) + +def Xform "root" { + def Xform "varying" ( + variants = { + string geo = "sphere" + } + prepend variantSets = "geo" + ) + { + variantSet "geo" = { + "cube" { + def Xform "CubePrim" + { + def Mesh "CubeMesh" ( + kind = "component" + ) + { + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4] + point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)] + color3f[] primvars:displayColor = [(0.13320851, 0.13320851, 0.13320851)] + int[] primvars:st:indices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 9, 8, 1, 10, 11, 3, 12, 0, 2, 13] + + double3 xformOp:translate = (2, 0, -2) + uniform token[] xformOpOrder = ["xformOp:translate"] + } + } + } + "sphere" { + def Xform "SpherePrim" + { + def Mesh "SphereMesh" ( + kind = "component" + ) + { + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4] + point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)] + color3f[] primvars:displayColor = [(0.13320851, 0.13320851, 0.13320851)] + int[] primvars:st:indices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 9, 8, 1, 10, 11, 3, 12, 0, 2, 13] + + double3 xformOp:translate = (-2, 0, 2) + uniform token[] xformOpOrder = ["xformOp:translate"] + } + } + + } + } + } +} + diff --git a/test/testSamples/variantInRef/layout/layout.usda b/test/testSamples/variantInRef/layout/layout.usda new file mode 100644 index 0000000000..b5cb7b365c --- /dev/null +++ b/test/testSamples/variantInRef/layout/layout.usda @@ -0,0 +1,7 @@ +#usda 1.0 +( + subLayers = [ + @assets/asset.usda@, + ] +) + diff --git a/test/testSamples/variantInRef/shot.usda b/test/testSamples/variantInRef/shot.usda new file mode 100644 index 0000000000..dab472e1e9 --- /dev/null +++ b/test/testSamples/variantInRef/shot.usda @@ -0,0 +1,8 @@ +#usda 1.0 +( + subLayers = [ + @layout/layout.usda@ + ] + upAxis = "Y" +) +