def setUp(self):
        ''' Called initially to set up the maya test environment '''
        # Load plugins
        self.assertTrue(self.pluginsLoaded)

        # Open top_layer.ma scene in test-samples
        mayaUtils.openTopLayerScene()

        # Create a proxy shape with empty stage to start with.
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        # Clear selection to start off.
        ufe.GlobalSelection.get().clear()

        # Select Ball_35.
        ball35Path = ufe.Path([
            mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"),
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")
        ])
        self.ball35Item = ufe.Hierarchy.createItem(ball35Path)

        ufe.GlobalSelection.get().append(self.ball35Item)

        # Create a ContextOps interface for it.
        self.contextOps = ufe.ContextOps.contextOps(self.ball35Item)
示例#2
0
    def testMayaGeomExtentsRecomputation(self):
        ''' Verify the automatic extents computation in when geom attributes change '''

        cmds.file(new=True, force=True)

        # create a Capsule via contextOps menu
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path('|stage1|stageShape1')
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        # capsule
        capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1')
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        capsulePrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(capsulePath))

        # get the height and extent "attributes"
        capsuleHeightAttr = capsulePrim.GetAttribute('height')
        capsuleExtentAttr = capsulePrim.GetAttribute('extent')

        self.assertAlmostEqual(capsuleHeightAttr.Get(), 1.0)
        capsuleExtent = capsuleExtentAttr.Get()
        expectedExtent = ((-0.5, -0.5, -1.0), (0.5, 0.5, 1.0))
        self.assertTrue(almostEqualBBox(capsuleExtent, expectedExtent))

        capsuleHeightAttr.Set(10.0)

        self.assertAlmostEqual(capsuleHeightAttr.Get(), 10.0)
        # Extent will have been recomputed:
        capsuleExtent = capsuleExtentAttr.Get()
        expectedExtent = ((-0.5, -0.5, -5.5), (0.5, 0.5, 5.5))
        self.assertTrue(almostEqualBBox(capsuleExtent, expectedExtent))
示例#3
0
    def testSingleAttributeBlocking(self):
        ''' Authoring attribute(s) in weaker layer(s) are not permitted if there exist opinion(s) in stronger layer(s).'''

        # create new stage
        cmds.file(new=True, force=True)
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        proxyShapeItem = ufe.Hierarchy.createItem(
            ufe.PathString.path(proxyShapePath))
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)

        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # create a prim. This creates the primSpec in the root layer.
        proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        # create a USD prim
        capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1')
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        capsulePrim = mayaUsd.ufe.ufePathToPrim(
            ufe.PathString.string(capsulePath))

        # author a new radius value
        self.assertTrue(capsulePrim.HasAttribute('radius'))
        radiusAttrUsd = capsulePrim.GetAttribute('radius')

        # authoring new attribute edit is expected to be allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(radiusAttrUsd))
        radiusAttrUsd.Set(10)

        # author a new axis value
        self.assertTrue(capsulePrim.HasAttribute('axis'))
        axisAttrUsd = capsulePrim.GetAttribute('axis')

        # authoring new attribute edit is expected to be allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(axisAttrUsd))
        axisAttrUsd.Set('Y')

        # create a sub-layer.
        rootLayer = stage.GetRootLayer()
        subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="LayerA")[0]

        # check to see the if the sublayers was added
        addedLayers = [subLayerA]
        self.assertEqual(rootLayer.subLayerPaths, addedLayers)

        # set the edit target to LayerA.
        cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerA)

        # radiusAttrUsd is not allowed to change since there is an opinion in a stronger layer
        self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(radiusAttrUsd))

        # axisAttrUsd is not allowed to change since there is an opinion in a stronger layer
        self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(axisAttrUsd))
示例#4
0
    def testCenterPivotUndo(self):

        cmds.file(new=True, force=True)

        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path('|stage1|stageShape1')
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1')
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        usdT3d = ufe.Transform3d.transform3d(capsuleItem)

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(capsuleItem)

        # center point is expected to be at [0.0, 0.0, 0.0]
        assertVectorAlmostEqual(self,
                                usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0])
        assertVectorAlmostEqual(self,
                                usdT3d.scalePivot().vector, [0.0, 0.0, 0.0])

        # move the pivot location
        cmds.move(7, 2, 1, r=True, urp=True, usp=True)

        assertVectorAlmostEqual(self,
                                usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0])
        assertVectorAlmostEqual(self,
                                usdT3d.scalePivot().vector, [7.0, 2.0, 1.0])

        # call center pivot command
        cmds.xform(cp=True)

        # center point is expected to be at [0.0, 0.0, 0.0]
        assertVectorAlmostEqual(self,
                                usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0])
        assertVectorAlmostEqual(self,
                                usdT3d.scalePivot().vector, [0.0, 0.0, 0.0])

        # undo
        cmds.undo()

        assertVectorAlmostEqual(self,
                                usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0])
        assertVectorAlmostEqual(self,
                                usdT3d.scalePivot().vector, [7.0, 2.0, 1.0])

        # redo
        cmds.redo()

        assertVectorAlmostEqual(self,
                                usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0])
        assertVectorAlmostEqual(self,
                                usdT3d.scalePivot().vector, [0.0, 0.0, 0.0])
示例#5
0
def getCleanMayaStage():
    """ gets a stage that only has an anon layer """

    cmds.file(new=True, force=True)
    mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

    proxyShapes = cmds.ls(type=PROXY_NODE_TYPE, long=True)
    shapePath = proxyShapes[0]
    stage = mayaUsd.lib.GetPrim(shapePath).GetStage()
    return shapePath, stage
示例#6
0
    def testFallbackCases(self):
        '''Fallback handler test cases.'''

        cmds.file(new=True, force=True)

        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        proxyShapePath = ufe.PathString.path('|stage1|stageShape1')
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Sphere'])

        spherePath = ufe.PathString.path('|stage1|stageShape1,/Sphere1')
        sphereItem = ufe.Hierarchy.createItem(spherePath)
        sphereT3d = ufe.Transform3d.transform3d(sphereItem)

        spherePrim = mayaUsd.ufe.ufePathToPrim(
            ufe.PathString.string(spherePath))
        sphereXformable = UsdGeom.Xformable(spherePrim)

        # Add transform ops that do not match either the Maya transform stack,
        # the USD common API transform stack, or a matrix stack.
        sphereXformable.AddTranslateOp()
        sphereXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat, "pivot")
        sphereXformable.AddRotateZOp()
        sphereXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat, "pivot",
                                       True)

        self.assertEqual(
            sphereXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(
                ("xformOp:translate", "xformOp:translate:pivot",
                 "xformOp:rotateZ", "!invert!xformOp:translate:pivot")))

        self.assertFalse(UsdGeom.XformCommonAPI(sphereXformable))
        self.assertFalse(mayaUsd.lib.XformStack.MayaStack().MatchingSubstack(
            sphereXformable.GetOrderedXformOps()))

        # Select sphere.
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(sphereItem)

        # Rotate sphere around X.
        cmds.rotate(30, 0, 0, r=True, os=True, fo=True)

        # Fallback interface will have added a RotXYZ transform op.
        self.assertEqual(
            sphereXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(
                ("xformOp:translate", "xformOp:translate:pivot",
                 "xformOp:rotateZ", "!invert!xformOp:translate:pivot",
                 "xformOp:rotateXYZ:maya_fallback")))
    def testCreateStageWithNewLayer(self):
        # Create a proxy shape with empty stage to start with.
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        # Verify that we got a proxy shape object.
        mayaSel = cmds.ls(sl=True)
        self.assertEqual(1, len(mayaSel))
        shapeNode = mayaSel[0]
        nt = cmds.nodeType(shapeNode)
        self.assertEqual('mayaUsdProxyShape', nt)

        # Verify that the shape node is connected to time.
        self.assertTrue(cmds.isConnected('time1.outTime', shapeNode + '.time'))
示例#8
0
    def testSessionLayer(self):
        '''Verify that the edit gets on the sessionLayer instead of the editTarget layer.'''

        import mayaUsd_createStageWithNewLayer

        proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
        )
        stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage()
        sessionLayer = stage.GetSessionLayer()
        prim = stage.DefinePrim('/A', 'Xform')

        primPathStr = proxyShapePathStr + ',/A'

        self.assertTrue(stage.GetSessionLayer().empty)

        with mayaUsd.lib.OpUndoItemList():
            self.assertTrue(
                mayaUsd.lib.PrimUpdaterManager.canEditAsMaya(primPathStr))
            self.assertTrue(
                mayaUsd.lib.PrimUpdaterManager.editAsMaya(primPathStr))

        self.assertFalse(stage.GetSessionLayer().empty)

        kPullPrimMetadataKey = "Maya:Pull:DagPath"
        self.assertEqual(prim.GetCustomDataByKey(kPullPrimMetadataKey),
                         "|__mayaUsd__|AParent|A")

        # Discard Maya edits, but there is nothing to discard.
        with mayaUsd.lib.OpUndoItemList():
            self.assertTrue(mayaUsd.lib.PrimUpdaterManager.discardEdits("A"))

        self.assertTrue(stage.GetSessionLayer().empty)

        self.assertEqual(prim.GetCustomDataByKey(kPullPrimMetadataKey), None)
示例#9
0
    def testCustomRigUpdaterAutoEditLoad(self):
        "Validate auto edit on stage load for CustomRig codeless schema"

        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        stage = mayaUsdUfe.getStage(proxyShape)
        self.assertTrue(stage)

        layer = stage.GetRootLayer()
        layer.ImportFromString(''' #sdf 1
            (
                defaultPrim = "world"
            )
            def Xform "world" {
                def Xform "anim" {
                    def CustomRig "bob" {
                        int cubes = 2
                        bool autoEdit = 1
                    }
                }
            }
        ''')

        self.assertTrue(self._GetMFnDagNode("bob|pCube1"))
        self.assertTrue(self._GetMFnDagNode("bob|pCube2"))

        bobPrim = stage.GetPrimAtPath("/world/anim/bob")
        autoEditAttr = bobPrim.GetAttribute("autoEdit")
        self.assertTrue(autoEditAttr.Get())
def getCleanMayaStage():
    """ gets a stage that only has an anon layer """

    cmds.file(new=True, force=True)
    shapePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
    stage = mayaUsd.lib.GetPrim(shapePath).GetStage()
    return shapePath, stage
示例#11
0
def createSimpleXformScene():
    '''Create a simple scene with a trivial hierarchy:

    A    translation (1, 2, 3)
    |_B  translation (7, 8, 9)

    '''

    psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
    psPath = ufe.PathString.path(psPathStr)
    ps = ufe.Hierarchy.createItem(psPath)
    stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
    aPrim = stage.DefinePrim('/A', 'Xform')
    aXformable = UsdGeom.Xformable(aPrim)
    aXlateOp = aXformable.AddTranslateOp()
    aXlation = Gf.Vec3d(1, 2, 3)
    aXlateOp.Set(aXlation)
    aUsdUfePathStr = psPathStr + ',/A'
    aUsdUfePath = ufe.PathString.path(aUsdUfePathStr)
    aUsdItem = ufe.Hierarchy.createItem(aUsdUfePath)

    bPrim = stage.DefinePrim('/A/B', 'Xform')
    bXformable = UsdGeom.Xformable(bPrim)
    bXlateOp = bXformable.AddTranslateOp()
    bXlation = Gf.Vec3d(7, 8, 9)
    bXlateOp.Set(bXlation)
    bUsdUfePathStr = aUsdUfePathStr + '/B'
    bUsdUfePath = ufe.PathString.path(bUsdUfePathStr)
    bUsdItem = ufe.Hierarchy.createItem(bUsdUfePath)

    return (ps, aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
            bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem)
示例#12
0
    def testDuplicateWithoutMaterials(self):
        '''Duplicate a Maya sphere without merging the materials.'''

        # Create a sphere.
        sphere = cmds.polySphere(r=1)

        # Create a stage to receive the USD duplicate.
        psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
        
        # Duplicate Maya data as USD data with materials
        cmds.mayaUsdDuplicate(cmds.ls(sphere, long=True)[0], psPathStr)

        # Verify that the copied sphere has a look (material) prim.
        looksPrim = stage.GetPrimAtPath("/pSphere1/Looks")
        self.assertTrue(looksPrim.IsValid())

        # Undo duplicate to USD.
        cmds.undo()

        # Duplicate Maya data as USD data without materials
        cmds.mayaUsdDuplicate(cmds.ls(sphere, long=True)[0], psPathStr, exportOptions='shadingMode=none')

        # Verify that the copied sphere does not have a look (material) prim.
        looksPrim = stage.GetPrimAtPath("/pSphere1/Looks")
        self.assertFalse(looksPrim.IsValid())
示例#13
0
    def testDuplicateAsUsdSameName(self):
        '''Duplicate a Maya transform to USD when USD already has a prim with that name.'''

        # Create a Maya transform named 'A'.
        mayaA = cmds.createNode('transform', name='A')
        cmds.setAttr(mayaA + '.translate', 1, 2, 3)

        # Create a stage to receive the USD duplicate, with a prim of the same name.
        psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
        aPrim = stage.DefinePrim('/A', 'Xform')
        
        # Duplicate Maya data as USD data.  As of 17-Nov-2021 no single-segment
        # path handler registered to UFE for Maya path strings, so use absolute
        # path.
        with mayaUsd.lib.OpUndoItemList():
            self.assertTrue(mayaUsd.lib.PrimUpdaterManager.duplicate(
                cmds.ls(mayaA, long=True)[0], psPathStr))

        # Maya hierarchy should be duplicated in USD, but with a numeric suffix due to the collision.
        usdNewAPathStr = psPathStr + ',/' + mayaA + '1'
        usdNewAPath = ufe.PathString.path(usdNewAPathStr)

        # Translations have been preserved.
        usdNewA = ufe.Hierarchy.createItem(usdNewAPath)
        usdNewAT3d = ufe.Transform3d.transform3d(usdNewA)
        self.assertEqual([1, 2, 3], usdNewAT3d.translation().vector)
示例#14
0
    def testBadSceneItem(self):
        '''Improperly constructed scene item should not crash Maya.'''

        # MAYA-112601 / GitHub #1169: improperly constructed Python scene item
        # should not cause a crash.
        cmds.file(new=True, force=True)

        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        proxyShapePath = ufe.PathString.path(proxyShape)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Sphere'])

        spherePath = ufe.PathString.path('%s,/Sphere1' % proxyShape)
        # The proper way to create a scene item is the following:
        #
        # sphereItem = ufe.Hierarchy.createItem(spherePath)
        #
        # A naive user can create a scene item as the following.  The resulting
        # scene item is not a USD scene item: it is a Python base class scene
        # item, which has a path but nothing else.  This should not a crash
        # when using the move command.
        sphereItem = ufe.SceneItem(spherePath)

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(sphereItem)

        cmds.move(0, 10, 0, relative=True, os=True, wd=True)
示例#15
0
 def setUp(self):
     # Start each test with a new scene with empty stage.
     cmds.file(new=True, force=True)
     import mayaUsd_createStageWithNewLayer
     self.proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
     )
     self.stage = mayaUsd.lib.GetPrim(self.proxyShapePathStr).GetStage()
    def testAnonymousRootToMaya(self):
        self.setupEmptyScene()

        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path(proxyShape)

        stage = mayaUsd.ufe.getStage(str(proxyShapePath))

        newPrimPath = "/ChangeInRoot"
        stage.DefinePrim(newPrimPath, "xform")
        self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid())

        stage.SetEditTarget(stage.GetSessionLayer())
        newSessionsPrimPath = "/ChangeInSession"
        stage.DefinePrim(newSessionsPrimPath, "xform")
        self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid())

        cmds.optionVar(intValue=('mayaUsd_SerializedUsdEditsLocation', 2))

        cmds.file(save=True, force=True, type='mayaAscii')
        cmds.file(new=True, force=True)
        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage('|stage1|stageShape1')
        self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid())
        self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid())

        shutil.rmtree(self._currentTestDir)
示例#17
0
    def testFactorySceneItem(self):
        '''A scene item can be constructed directly and will work.'''

        # Was:
        # MAYA-112601 / GitHub #1169: improperly constructed Python scene item
        # should not cause a crash.
        # Now:
        # MAYA-113409 / GutHub PR #222: Use factory CTOR for directly constructed
        # SceneItems.
        cmds.file(new=True, force=True)

        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        proxyShapePath = ufe.PathString.path(proxyShape)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Sphere'])

        spherePath = ufe.PathString.path('%s,/Sphere1' % proxyShape)
        sphereItem = ufe.SceneItem(spherePath)

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(sphereItem)

        cmds.move(0, 10, 0, relative=True, os=True, wd=True)

        # The USD prim will have a transform since the move command worked:
        sphereAttrs = ufe.Attributes.attributes(sphereItem)
        self.assertIsNotNone(sphereAttrs)
        self.assertTrue("xformOp:translate" in sphereAttrs.attributeNames)
示例#18
0
    def testPrimPropertyPathNotifs(self):
        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path(proxyShape)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        # Select the capsule
        capPath = ufe.PathString.path('%s,/Capsule1' % proxyShape)
        capItem = ufe.Hierarchy.createItem(capPath)
        ufe.GlobalSelection.get().clear()
        ufe.GlobalSelection.get().append(capItem)

        # No notifications yet.
        obs = TestObserver()
        ufe.Attributes.addObserver(capItem, obs)
        ufe.Transform3d.addObserver(capItem, obs)
        self.assertEqual(obs.notifications, 0)

        # Move the capsule
        cmds.move(0, 10, 10)

        # Verify that we got both ValueChanged and Transform3d notifs.
        # Note: we should get notifs on both the "xformOp:translate" and
        #       "xformOpOrder" attributes. We don't care how many, just that
        #       we are getting both of these notifs kinds on both the move
        #       and undo.
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)

        # Reset observer and then undo and again verify notifs.
        obs.reset()
        self.assertEqual(obs.notifications, 0)
        cmds.undo()
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)

        # Reset and test same thing with Rotate.
        obs.reset()
        cmds.rotate(10, 0, 0)
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)
        obs.reset()
        self.assertEqual(obs.notifications, 0)
        cmds.undo()
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)

        # Reset and test same thing with Scale.
        obs.reset()
        cmds.scale(2, 2, 2)
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)
        obs.reset()
        self.assertEqual(obs.notifications, 0)
        cmds.undo()
        self.assertTrue(obs.nbValueChanged > 0)
        self.assertTrue(obs.nbTransform3d > 0)
示例#19
0
    def testPurposeBoundingBox(self):
        '''Bounding box of prims with guide, proxy, and render purpose.'''
        # Create a scene with prims of purposes other than default: guide,
        # proxy, and render.  All must have a valid bounding box.  The bounding
        # box is conditional to the proxy shape on the UFE path to the prim
        # having that purpose enabled: if the purpose is disabled, the bounding
        # box is invalid.
        import mayaUsd_createStageWithNewLayer

        proxyShapePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
        )
        proxyShapePathSegment = mayaUtils.createUfePathSegment(proxyShapePath)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        usdPaths = ['/Cube1', '/Cube2', '/Cube3']
        prims = [stage.DefinePrim(path, 'Cube') for path in usdPaths]
        purposes = [
            UsdGeom.Tokens.proxy, UsdGeom.Tokens.guide, UsdGeom.Tokens.render
        ]
        for (prim, purpose) in zip(prims, purposes):
            imageable = UsdGeom.Imageable(prim)
            imageable.CreatePurposeAttr(purpose)

        # Create a UFE scene item for each prim, and get the bounding box using
        # the Object3d interface.
        for (prim, usdPath) in zip(prims, usdPaths):
            pathSegment = usdUtils.createUfePathSegment(usdPath)
            path = ufe.Path([proxyShapePathSegment, pathSegment])
            item = ufe.Hierarchy.createItem(path)
            object3d = ufe.Object3d.object3d(item)

            # First turn off proxy, guide, render purposes on the proxy shape.
            # The bounding box should be invalid.
            purposeAttribs = [
                'drawProxyPurpose', 'drawGuidePurpose', 'drawRenderPurpose'
            ]
            for purposeAttrib in purposeAttribs:
                cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 0)

            bbox = object3d.boundingBox()

            self.assertTrue(bbox.empty())

            # Next, turn on each purpose in turn on the proxy shape.  The
            # bounding box should be valid only if the prim's purpose matches
            # the proxy shape purpose.
            imageable = UsdGeom.Imageable(prim)
            primPurpose = imageable.GetPurposeAttr().Get()
            for (purpose, purposeAttrib) in zip(purposes, purposeAttribs):
                cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 1)

                bbox = object3d.boundingBox()

                if primPurpose == purpose:
                    assertVectorAlmostEqual(self, bbox.min.vector, [-1] * 3)
                    assertVectorAlmostEqual(self, bbox.max.vector, [1] * 3)
                else:
                    self.assertTrue(bbox.empty())

                cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 0)
示例#20
0
    def testDuplicateAsUsdUndoRedo(self):
        '''Duplicate a Maya transform hierarchy to USD and then undo and redo the command.'''

        # Create a hierarchy.  Because group1 is selected upon creation, group2
        # will be its parent.
        group1 = cmds.createNode('transform')
        group2 = cmds.group()
        self.assertEqual(cmds.listRelatives(group1, parent=True)[0], group2)

        cmds.setAttr(group1 + '.translate', 1, 2, 3)
        cmds.setAttr(group2 + '.translate', -4, -5, -6)

        # Create a stage to receive the USD duplicate.
        psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        
        # Duplicate Maya data as USD data.  As of 17-Nov-2021 no single-segment
        # path handler registered to UFE for Maya path strings, so use absolute
        # path.
        cmds.mayaUsdDuplicate(cmds.ls(group2, long=True)[0], psPathStr)

        def verifyDuplicate():
            # Maya hierarchy should be duplicated in USD.
            usdGroup2PathStr = psPathStr + ',/' + group2
            usdGroup1PathStr = usdGroup2PathStr + '/' + group1
            usdGroup2Path = ufe.PathString.path(usdGroup2PathStr)
            usdGroup1Path = ufe.PathString.path(usdGroup1PathStr)

            # group1 is the child of group2
            usdGroup1 = ufe.Hierarchy.createItem(usdGroup1Path)
            usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path)
            usdGroup1Hier = ufe.Hierarchy.hierarchy(usdGroup1)
            usdGroup2Hier = ufe.Hierarchy.hierarchy(usdGroup2)
            self.assertEqual(usdGroup2, usdGroup1Hier.parent())
            self.assertEqual(len(usdGroup2Hier.children()), 1)
            self.assertEqual(usdGroup1, usdGroup2Hier.children()[0])

            # Translations have been preserved.
            usdGroup1T3d = ufe.Transform3d.transform3d(usdGroup1)
            usdGroup2T3d = ufe.Transform3d.transform3d(usdGroup2)
            self.assertEqual([1, 2, 3], usdGroup1T3d.translation().vector)
            self.assertEqual([-4, -5, -6], usdGroup2T3d.translation().vector)

        verifyDuplicate()

        cmds.undo()

        def verifyDuplicateIsGone():
            # Maya hierarchy should no longer be duplicated in USD.
            usdGroup2PathStr = psPathStr + ',/' + group2
            usdGroup2Path = ufe.PathString.path(usdGroup2PathStr)
            usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path)
            self.assertIsNone(usdGroup2)

        verifyDuplicateIsGone()

        cmds.redo()

        verifyDuplicate()
示例#21
0
    def testZAttrChangeRedoAfterPrimCreateRedo(self):
        '''Redo attribute change after redo of prim creation.'''
        cmds.file(new=True, force=True)

        # Create a capsule, change one of its attributes.
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path('|stage1|stageShape1')
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        cmd = proxyShapeContextOps.doOpCmd(['Add New Prim', 'Capsule'])
        ufeCmd.execute(cmd)

        capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1')
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)

        # Create the attributes interface for the item.
        attrs = ufe.Attributes.attributes(capsuleItem)
        self.assertIsNotNone(attrs)
        self.assertTrue(attrs.hasAttribute('radius'))
        radiusAttr = attrs.attribute('radius')

        oldRadius = radiusAttr.get()

        ufeCmd.execute(radiusAttr.setCmd(2))

        newRadius = radiusAttr.get()

        self.assertEqual(newRadius, 2)
        self.assertNotEqual(oldRadius, newRadius)

        # Undo 2x: undo attr change and prim creation.
        cmds.undo()
        cmds.undo()

        # Redo 2x: prim creation, attr change.
        cmds.redo()
        cmds.redo()

        # Re-create item, as its underlying prim was re-created.
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        attrs = ufe.Attributes.attributes(capsuleItem)
        radiusAttr = attrs.attribute('radius')

        self.assertEqual(radiusAttr.get(), newRadius)
示例#22
0
    def testMayaShapeBBoxCacheClearing(self):
        ''' Verify that the bounding box cache gets cleared'''

        cmds.file(new=True, force=True)

        # create a Capsule via contextOps menu
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path('|stage1|stageShape1')
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Xform'])
        xformPath = ufe.PathString.path('|stage1|stageShape1,/Xform1')
        xformItem = ufe.Hierarchy.createItem(xformPath)
        xformObject3d = ufe.Object3d.object3d(xformItem)
        proxyShapeContextOps = ufe.ContextOps.contextOps(xformItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Sphere'])
        proxyShapeContextOps.doOp(['Add New Prim', 'Sphere'])
        selectionList = OpenMaya.MSelectionList()
        selectionList.add('|stage1|stageShape1')
        shapeNode = OpenMaya.MFnDagNode(selectionList.getDependNode(0))

        # Two spheres at origin, the bounding box is the unit cube:
        expectedBBox = ((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0))
        self.assertTrue(almostEqualBBox(xformObject3d.boundingBox(), expectedBBox))
        # Shape BBox should be the same (and will be cached):
        self.assertTrue(almostEqualBBox(shapeNode.boundingBox, expectedBBox))

        sphere1Path = ufe.PathString.path('|stage1|stageShape1,/Xform1/Sphere1')
        sphere1Item = ufe.Hierarchy.createItem(sphere1Path)
        sphere1Prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(sphere1Path))
        UsdGeom.XformCommonAPI(sphere1Prim).SetTranslate((-5, 0, 0))

        sphere2Path = ufe.PathString.path('|stage1|stageShape1,/Xform1/Sphere2')
        sphere2Item = ufe.Hierarchy.createItem(sphere2Path)
        sphere2Prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(sphere2Path))
        UsdGeom.XformCommonAPI(sphere2Prim).SetTranslate((0, 5, 0))

        expectedBBox = ((-6.0, -1.0, -1.0), (1.0, 6.0, 1.0))
        self.assertTrue(almostEqualBBox(xformObject3d.boundingBox(), expectedBBox))
        # The next test will only work if the cache was cleared when translating
        # the spheres:
        self.assertTrue(almostEqualBBox(shapeNode.boundingBox, expectedBBox))
示例#23
0
    def createLayer(self):
        '''
        Helper that creates a stage and layer and return the item of its shape item.
        '''
        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path(proxyShape)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)

        return proxyShapeItem, proxyShapePath
示例#24
0
    def testAddNewPrimWithDelete(self):
        cmds.file(new=True, force=True)

        # Create a proxy shape with empty stage to start with.
        import mayaUsd_createStageWithNewLayer
        mayaUsd_createStageWithNewLayer.createStageWithNewLayer()

        # Create a ContextOps interface for the proxy shape.
        proxyShapePath = ufe.Path(
            [mayaUtils.createUfePathSegment("|world|stage1|stageShape1")])
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        contextOps = ufe.ContextOps.contextOps(proxyShapeItem)

        # Add a new Xform prim.
        cmd = contextOps.doOpCmd(['Add New Prim', 'Xform'])
        self.assertIsNotNone(cmd)
        ufeCmd.execute(cmd)

        # The proxy shape should now have a single UFE child item.
        proxyShapehier = ufe.Hierarchy.hierarchy(proxyShapeItem)
        self.assertTrue(proxyShapehier.hasChildren())
        self.assertEqual(len(proxyShapehier.children()), 1)

        # Using UFE, delete this new prim (which doesn't actually delete it but
        # instead makes it inactive).
        cmds.pickWalk(d='down')
        cmds.delete()

        # The proxy shape should now have no UFE child items (since we skip inactive).
        self.assertFalse(proxyShapehier.hasChildren())
        self.assertEqual(len(proxyShapehier.children()), 0)

        # Add another Xform prim (which should get a unique name taking into
        # account the prim we just made inactive).
        cmd = contextOps.doOpCmd(['Add New Prim', 'Xform'])
        self.assertIsNotNone(cmd)
        ufeCmd.execute(cmd)

        # The proxy shape should now have a single UFE child item.
        self.assertTrue(proxyShapehier.hasChildren())
        self.assertEqual(len(proxyShapehier.children()), 1)
示例#25
0
def createStage():
    ''' create a simple stage '''
    cmds.file(new=True, force=True)
    import mayaUsd_createStageWithNewLayer
    proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
    )
    proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
    proxyShapePath = proxyShapes[0]
    proxyShapeItem = ufe.Hierarchy.createItem(
        ufe.PathString.path(proxyShapePath))
    proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
    stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
    return (stage, proxyShapePathStr, proxyShapeItem, proxyShapeContextOps)
示例#26
0
    def testEditRouter(self):
        '''Test edit router functionality.'''

        cmds.file(new=True, force=True)
        import mayaUsd_createStageWithNewLayer

        # Create the following hierarchy:
        #
        # ps
        #  |_ A
        #      |_ B
        #
        # We A and duplicate it.

        psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
        stage.DefinePrim('/A', 'Xform')
        stage.DefinePrim('/A/B', 'Xform')

        psPath = ufe.PathString.path(psPathStr)
        psPathSegment = psPath.segments[0]
        aPath = ufe.Path([psPathSegment, usdUtils.createUfePathSegment('/A')])
        a = ufe.Hierarchy.createItem(aPath)
        bPath = aPath + ufe.PathComponent('B')
        b = ufe.Hierarchy.createItem(bPath)

        # Add a sub-layer, where the parent edit should write to.
        subLayerId = cmds.mayaUsdLayerEditor(stage.GetRootLayer().identifier,
                                             edit=True,
                                             addAnonymous="aSubLayer")[0]

        mayaUsd.lib.registerEditRouter('duplicate', firstSubLayer)

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(a)

        cmds.duplicate()

        sublayer01 = Sdf.Find(subLayerId)
        self.assertIsNotNone(sublayer01)
        self.assertIsNotNone(sublayer01.GetPrimAtPath('/A1/B'))
示例#27
0
    def testIllegalEditAsMaya(self):
        '''Trying to edit as Maya on object that doesn't support it.'''

        import mayaUsd_createStageWithNewLayer

        proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
        )
        stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage()
        blendShape = stage.DefinePrim('/BlendShape1', 'BlendShape')
        scope = stage.DefinePrim('/Scope1', 'Scope')

        blendShapePathStr = proxyShapePathStr + ',/BlendShape1'
        scopePathStr = proxyShapePathStr + ',/Scope1'

        # Blend shape cannot be edited as Maya: it has no importer.
        with mayaUsd.lib.OpUndoItemList():
            self.assertFalse(
                mayaUsd.lib.PrimUpdaterManager.canEditAsMaya(
                    blendShapePathStr))
            self.assertFalse(
                mayaUsd.lib.PrimUpdaterManager.editAsMaya(blendShapePathStr))
    def testAnonymousRootToUsd(self):
        self.setupEmptyScene()

        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path(proxyShape)

        stage = mayaUsd.ufe.getStage(str(proxyShapePath))

        newPrimPath = "/ChangeInRoot"
        stage.DefinePrim(newPrimPath, "xform")
        self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid())

        stage.SetEditTarget(stage.GetSessionLayer())
        newSessionsPrimPath = "/ChangeInSession"
        stage.DefinePrim(newSessionsPrimPath, "xform")
        self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid())

        cmds.optionVar(intValue=('mayaUsd_SerializedUsdEditsLocation', 1))

        msg = ("Session Layer before: " + stage.GetSessionLayer().identifier)
        stage = None

        cmds.file(save=True, force=True, type='mayaAscii')
        cmds.file(new=True, force=True)
        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsdLib.GetPrim('|stage1|stageShape1').GetStage()
        msg += ("    Session Layer after: " +
                stage.GetSessionLayer().identifier)
        self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid())

        # Temporarily disabling this check while investigating why it can fail on certain build combinations
        # self.assertFalse(stage.GetPrimAtPath(
        #     newSessionsPrimPath).IsValid(), msg)

        cmds.file(new=True, force=True)
        shutil.rmtree(self._currentTestDir)
示例#29
0
    def testMayaSelectSwitchVariant(self):
        '''Stale selection items must be removed on variant switch.'''

        import mayaUsd_createStageWithNewLayer
        import maya.internal.ufeSupport.ufeCmdWrapper as ufeCmd

        # Create a scene with two variants.
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        stage = mayaUsd.lib.GetPrim(proxyShape).GetStage()
        top = stage.DefinePrim('/Xform1', 'Xform')
        vset = top.GetVariantSets().AddVariantSet('modelingVariant')
        vset.AddVariant('cube')
        vset.AddVariant('sphere')
        vset.SetVariantSelection('cube')
        with vset.GetVariantEditContext():
            stage.DefinePrim('/Xform1/Cube', 'Cube')
        vset.SetVariantSelection('sphere')
        with vset.GetVariantEditContext():
            stage.DefinePrim('/Xform1/Sphere', 'Sphere')

        # The sphere is the sole child of Xform1.  Get an attribute from it,
        # select it.
        xformPath = ufe.PathString.path('%s,/Xform1' % proxyShape)
        spherePath = ufe.PathString.path('%s,/Xform1/Sphere' % proxyShape)
        xformItem = ufe.Hierarchy.createItem(xformPath)
        sphereItem = ufe.Hierarchy.createItem(spherePath)

        xformHier = ufe.Hierarchy.hierarchy(xformItem)
        xformChildren = xformHier.children()
        self.assertEqual(len(xformChildren), 1)
        self.assertEqual(xformChildren[0].path(), spherePath)
        sphereAttrs = ufe.Attributes.attributes(sphereItem)
        sphereRadius = sphereAttrs.attribute('radius')
        self.assertEqual(sphereRadius.get(), 1)

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(sphereItem)

        self.assertEqual(len(sn), 1)

        # Switch variants using a command: the cube is now the sole child of
        # Xform1, we can get an attribute from the cube.  The selection must
        # now be empty.
        xformCtxOps = ufe.ContextOps.contextOps(xformItem)
        cmd = xformCtxOps.doOpCmd(['Variant Sets', 'modelingVariant', 'cube'])
        ufeCmd.execute(cmd)

        cubePath = ufe.PathString.path('%s,/Xform1/Cube' % proxyShape)
        cubeItem = ufe.Hierarchy.createItem(cubePath)

        xformChildren = xformHier.children()
        self.assertEqual(len(xformChildren), 1)
        self.assertEqual(xformChildren[0].path(), cubePath)
        cubeAttrs = ufe.Attributes.attributes(cubeItem)
        cubeRadius = cubeAttrs.attribute('size')
        self.assertEqual(cubeRadius.get(), 2)

        self.assertTrue(sn.empty())

        # Undo: selection is restored, seletion item is valid.
        cmds.undo()

        self.assertEqual(len(sn), 1)
        sphereItem = sn.front()
        self.assertEqual(sphereItem.path(), spherePath)
        sphereAttrs = ufe.Attributes.attributes(sphereItem)
        sphereRadius = sphereAttrs.attribute('radius')
        self.assertEqual(sphereRadius.get(), 1)
        xformChildren = xformHier.children()
        self.assertEqual(len(xformChildren), 1)
        self.assertEqual(xformChildren[0].path(), spherePath)

        # Redo: selection is cleared.
        cmds.redo()

        self.assertTrue(sn.empty())
        xformChildren = xformHier.children()
        self.assertEqual(len(xformChildren), 1)
        self.assertEqual(xformChildren[0].path(), cubePath)

        # Undo: selection restored to sphere.
        cmds.undo()

        self.assertEqual(len(sn), 1)
        sphereItem = sn.front()
        self.assertEqual(sphereItem.path(), spherePath)

        # Now set the variant outside a Maya command.  Stale scene items must be
        # removed from the selection.
        vset.SetVariantSelection('cube')

        self.assertTrue(sn.empty())
示例#30
0
    def testMayaSelectMuteLayer(self):
        '''Stale selection items must be removed on mute layer.'''

        # Create new stage
        import mayaUsd_createStageWithNewLayer
        proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer(
        )
        proxyShapePath = ufe.PathString.path(proxyShapePathStr)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)

        # Create a sub-layer.
        stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage()
        rootLayer = stage.GetRootLayer()
        subLayerId = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                             edit=True,
                                             addAnonymous="Layer1")[0]

        # Set the edit target to new sub-layer.
        cmds.mayaUsdEditTarget(proxyShapePathStr,
                               edit=True,
                               editTarget=subLayerId)

        # Create a prim.  This will create the primSpec in the new sub-layer.
        proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        capsulePathStr = '%s,/Capsule1' % proxyShapePathStr
        capsulePath = ufe.PathString.path(capsulePathStr)
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)

        # Select the prim.  This is the core of the test: on subtree invalidate,
        # the prim's UFE scene item should be removed from the selection.
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(capsuleItem)

        self.assertTrue(sn.contains(capsulePath))

        # Mute sub-layer
        cmds.mayaUsdLayerEditor(subLayerId,
                                edit=True,
                                muteLayer=[1, proxyShapePathStr])

        # Should be nothing on the selection list.
        self.assertTrue(sn.empty())

        # Undo: capsule should be back on the selection list.
        cmds.undo()

        self.assertTrue(sn.contains(capsulePath))

        # Redo: selection list now empty.
        cmds.redo()

        self.assertTrue(sn.empty())

        cmds.undo()

        # Change attribute on the capsule, using the item from the selection,
        # which must be valid.
        self.assertTrue(len(sn), 1)
        capsuleItem = sn.front()
        capsuleAttrs = ufe.Attributes.attributes(capsuleItem)
        self.assertIsNotNone(capsuleAttrs)

        capsuleRadius = capsuleAttrs.attribute('radius')
        capsuleRadius.set(2)

        self.assertEqual(capsuleRadius.get(), 2)

        # Now mute the layer outside a Maya command.  Stale scene items must be
        # removed from the selection.
        self.assertTrue(len(sn), 1)
        self.assertTrue(sn.contains(capsulePath))

        stage.MuteLayer(subLayerId)

        self.assertTrue(sn.empty())