コード例 #1
0
 def __call__(self, notification):
     if (ufeUtils.ufeFeatureSetVersion() >= 2):
         if isinstance(notification, ufe.AttributeValueChanged):
             self._notifications += 1
     else:
         if isinstance(notification, ufe.AttributeChanged):
             self._notifications += 1
コード例 #2
0
def getPrimFromSceneItem(item):
    if ufeUtils.ufeFeatureSetVersion() >= 2:
        rawItem = item.getRawAddress()
        prim = mayaUsd.ufe.getPrimFromRawItem(rawItem)
        return prim
    else:
        return Usd.Prim()
コード例 #3
0
ファイル: mayaUtils.py プロジェクト: mattyjams/maya-usd
def createUfePathSegment(mayaPath):
    """
        Create a UFE path from a given maya path and return the first segment.
        Args:
            mayaPath (str): The maya path to use
        Returns :
            PathSegment of the given mayaPath
    """
    if ufeUtils.ufeFeatureSetVersion() >= 2:
        return ufe.PathString.path(mayaPath).segments[0]
    else:
        if not mayaPath.startswith("|world"):
            mayaPath = "|world" + mayaPath
        return ufe.PathSegment(mayaPath, mayaUsdUfe.getMayaRunTimeId(),
                               mayaSeparator)
コード例 #4
0
class DeleteCmdTestCase(unittest.TestCase):
    '''Verify the Maya delete command, for multiple runtimes.

    UFE Feature : SceneItemOps
    Maya Feature : delete
    Action : Remove object from scene.
    Applied On Selection / Command Arguments:
        - Multiple Selection [Mixed, Non-Maya].  Maya-only selection tested by
          Maya.
    Undo/Redo Test : Yes
    Expect Results To Test :
        - Delete removes object from scene.
    Edge Cases :
        - None.
    '''

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

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

        # Open top_layer.ma scene in testSamples
        mayaUtils.openTopLayerScene()

        # Create some extra Maya nodes
        cmds.polySphere()

        # Clear selection to start off
        cmds.select(clear=True)

    def testDelete(self):
        '''Delete Maya and USD objects.'''

        # Create our UFE notification observer
        ufeObs = TestObserver()

        if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '2021'):
            ufe.Scene.addObjectDeleteObserver(ufeObs)
            ufe.Scene.addObjectAddObserver(ufeObs)
        else:
            ufe.Scene.addObserver(ufeObs)

        # Select two objects, one Maya, one USD.
        spherePath = ufe.Path(mayaUtils.createUfePathSegment("|pSphere1"))
        sphereItem = ufe.Hierarchy.createItem(spherePath)
        sphereShapePath = ufe.Path(
            mayaUtils.createUfePathSegment("|pSphere1|pSphereShape1"))
        sphereShapeItem = ufe.Hierarchy.createItem(sphereShapePath)

        mayaSegment = mayaUtils.createUfePathSegment("|transform1|proxyShape1")
        ball35Path = ufe.Path([
            mayaSegment,
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")
        ])
        ball35Item = ufe.Hierarchy.createItem(ball35Path)
        propsPath = ufe.Path(
            [mayaSegment,
             usdUtils.createUfePathSegment("/Room_set/Props")])
        propsItem = ufe.Hierarchy.createItem(propsPath)

        sphereShapeName = str(sphereShapeItem.path().back())
        ball35Name = str(ball35Item.path().back())

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

        # Before delete, each item is a child of its parent.
        sphereHierarchy = ufe.Hierarchy.hierarchy(sphereItem)
        propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)

        ufeObs.reset()
        cmds.delete()

        # We deleted two items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 2)
        self.assertEqual(ufeObs.nbAddNotif(), 0)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)

        cmds.undo()

        # After the undo we added two items back.
        self.assertEqual(ufeObs.nbDeleteNotif(), 2)
        self.assertEqual(ufeObs.nbAddNotif(), 2)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)

        cmds.redo()

        # After the redo we again deleted two items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 4)
        self.assertEqual(ufeObs.nbAddNotif(), 2)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)

        # undo to restore state to original.
        cmds.undo()

        # After the undo we again added two items back.
        self.assertEqual(ufeObs.nbDeleteNotif(), 4)
        self.assertEqual(ufeObs.nbAddNotif(), 4)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2,
                         'testDeleteArgs only available in UFE v2 or greater.')
    def testDeleteArgs(self):
        '''Delete Maya and USD objects passed as command arguments.'''

        # Create our UFE notification observer
        ufeObs = TestObserver()
        if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '2021'):
            ufe.Scene.addObjectDeleteObserver(ufeObs)
            ufe.Scene.addObjectAddObserver(ufeObs)
        else:
            ufe.Scene.addObserver(ufeObs)

        spherePath = ufe.Path(mayaUtils.createUfePathSegment("|pSphere1"))
        sphereItem = ufe.Hierarchy.createItem(spherePath)
        sphereShapePath = ufe.Path(
            mayaUtils.createUfePathSegment("|pSphere1|pSphereShape1"))
        sphereShapeItem = ufe.Hierarchy.createItem(sphereShapePath)

        mayaSegment = mayaUtils.createUfePathSegment("|transform1|proxyShape1")
        ball35Path = ufe.Path([
            mayaSegment,
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")
        ])
        ball35Item = ufe.Hierarchy.createItem(ball35Path)
        ball34Path = ufe.Path([
            mayaSegment,
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_34")
        ])
        ball34Item = ufe.Hierarchy.createItem(ball34Path)
        propsPath = ufe.Path(
            [mayaSegment,
             usdUtils.createUfePathSegment("/Room_set/Props")])
        propsItem = ufe.Hierarchy.createItem(propsPath)

        sphereShapeName = str(sphereShapeItem.path().back())
        ball35Name = str(ball35Item.path().back())
        ball34Name = str(ball34Item.path().back())

        # Before delete, each item is a child of its parent.
        sphereHierarchy = ufe.Hierarchy.hierarchy(sphereItem)
        propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)

        ball34PathString = ufe.PathString.string(ball34Path)
        self.assertEqual(ball34PathString,
                         "|transform1|proxyShape1,/Room_set/Props/Ball_34")

        # Test that "|world" prefix is optional for multi-segment paths.
        ball35PathString = "|transform1|proxyShape1,/Room_set/Props/Ball_35"

        ufeObs.reset()
        cmds.delete(ball35PathString, ball34PathString,
                    "|pSphere1|pSphereShape1")

        # We deleted 3 items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 3)
        self.assertEqual(ufeObs.nbAddNotif(), 0)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)
        self.assertNotIn(ball34Name, propsChildrenNames)
        self.assertFalse(cmds.objExists("|pSphere1|pSphereShape1"))

        cmds.undo()

        # After the undo we added three items back.
        self.assertEqual(ufeObs.nbDeleteNotif(), 3)
        self.assertEqual(ufeObs.nbAddNotif(), 3)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(ball34Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)
        self.assertIn(ball34Name, propsChildrenNames)
        self.assertTrue(cmds.objExists("|pSphere1|pSphereShape1"))

        cmds.redo()

        # After the redo we again deleted three items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 6)
        self.assertEqual(ufeObs.nbAddNotif(), 3)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)
        self.assertNotIn(ball34Name, propsChildrenNames)
        self.assertFalse(cmds.objExists("|pSphere1|pSphereShape1"))
コード例 #5
0
class ComboCmdTestCase(testTRSBase.TRSTestCaseBase):
    '''Verify the Transform3d UFE interface, for multiple runtimes.

    The Maya move, rotate, and scale commands is used to test setting object
    translation, rotation, and scale.
    As of 05-May-2020, object space relative moves, rotates, and scales are
    supported by Maya code, and move and rotate are supported in world space
    as well, although scale is not supported in world space.
    Object translation, rotation, and scale is read using the Transform3d
    interface and the native run-time interface.

    This test performs a sequence of the possible types of operations, and
    verifies that the position, rotation, and scale of the object has been
    modified according to how such operations should cumulate.

    The expected value consists of the translate, rotate, and scale vectors
    (in world space). It is computed by:
        - initializing the translate, rotate, and scale vectors
        - calling updateTRS after each operation; this method will reassemble
          the transformation matrix from the three vectors, apply the
          appropriate matrix transformation for the given operation, in the
          given space, and extract the translate, rotate, and scale vector,
          once again in world space.

    When a snapshot is taken for comparison purposes, the value extracted from
    the runtime objects is extracted for each component, and assembled into a
    vector that can be compared to the computed expected value vector.
    
    UFE Feature : Transform3d
    Maya Feature : move, rotate, scale
    Action : Relative move, rotate, and scale in object space; move, rotate in
    object space.
    Applied On Selection :
        - No selection - Given node as param
        - Single Selection [Maya, Non-Maya]
    Undo/Redo Test : Yes
    Expect Results To Test :
        - Maya Dag object world space position.
        - USD object world space position.
    Edge Cases :
        - None.
    '''

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        fixturesUtils.setUpClass(__file__, loadPlugin=False)

        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

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

        # Set up memento, a list of snapshots.
        self.memento = []

        # Callables to get current object translation and rotation using the
        # run-time and UFE.
        self.move = 0
        self.rotate = 1
        self.scale = 2
        self.ops = [self.move, self.rotate, self.scale]
        self.runTimes = [None, None, None]
        self.ufes = [None, None, None]

        self.noSpace = None
        self.spaces = [om.MSpace.kObject, om.MSpace.kWorld]

        # Open top_layer.ma scene in testSamples
        mayaUtils.openTopLayerScene()

        # Create some extra Maya nodes
        cmds.polySphere()
        cmds.polyCube()

        # Clear selection to start off
        cmds.select(clear=True)

    def updateTRS(self, expectedTRS, op, v, space=om.MSpace.kWorld):
        '''Update the expected vector based on given operation, vector and space
        The expectedTRS vector has 9 entries:
        * 0-2 The world position
        * 3-5 The world rotation (in degrees)
        * 6-8 The world scale
        The possible operations are move, rotate, and scale.
        The possible spaces are kObject and kWorld (default)
        '''
        if (expectedTRS is None):
            expectedTRS = [None] * 9
        # trs starts as the identity matrix
        #
        trs = om.MTransformationMatrix()

        # Add translation, rotation, and scale, in world space, to recreate
        # the last transformation matrix.
        #
        if (expectedTRS[0] is not None):
            trs.setTranslation(
                om.MVector(expectedTRS[0], expectedTRS[1], expectedTRS[2]),
                om.MSpace.kWorld)
        if (expectedTRS[3] is not None):
            trs.setRotation(
                om.MEulerRotation(radians(expectedTRS[3]),
                                  radians(expectedTRS[4]),
                                  radians(expectedTRS[5])))
        if (expectedTRS[6] is not None):
            trs.setScale(
                om.MVector(expectedTRS[6], expectedTRS[7], expectedTRS[8]),
                om.MSpace.kWorld)
        # Apply the requested operation. If the space is kObject, and we had a
        # scale factor, we must counteract it to get the right matrix, by
        # dividing the translation vector by it (otherwise it ends up being
        # scaled twice, and the expected value is incorrect).
        #
        if op == self.move:
            if (space == om.MSpace.kObject and expectedTRS[6] is not None):
                trs.translateBy(
                    om.MVector(v[0] / expectedTRS[6], v[1] / expectedTRS[7],
                               v[2] / expectedTRS[8]), space)
            else:
                trs.translateBy(om.MVector(v[0], v[1], v[2]), space)
        elif op == self.rotate:
            trs.rotateBy(
                om.MEulerRotation(radians(v[0]), radians(v[1]), radians(v[2])),
                space)
        elif op == self.scale:
            trs.scaleBy(om.MVector(v[0], v[1], v[2]), space)
        # Recover the world space translate, rotate, and scale, and updated
        # the expected vector
        #
        expectedTRS[0:3] = trs.translation(om.MSpace.kWorld)
        r = trs.rotation().asVector()
        expectedTRS[3] = degrees(r[0])
        expectedTRS[4] = degrees(r[1])
        expectedTRS[5] = degrees(r[2])
        expectedTRS[6:9] = trs.scale(om.MSpace.kWorld)
        return expectedTRS

    def extractTRS(self, expectedTRS, op):
        '''Extract the move, rotate, or scale component
        '''
        if op == self.move:
            # Translation (x, y, z)
            #
            return expectedTRS[0:3]
        elif op == self.rotate:
            # Rotation vector in degrees (x, y, z)
            #
            return expectedTRS[3:6]
        elif op == self.scale:
            # Scale (x, y, z)
            #
            return expectedTRS[6:9]

    def snapshotRunTimeUFE(self):
        '''Return a pair with an op read from the run-time and from UFE.

        Tests that the op read from the run-time interface matches the
        UFE op.
        '''
        # Get translation
        #
        rtAll = None
        ufeAll = None
        offset = 0
        for op in self.ops:
            runTimeVec = self.runTimes[op]()
            ufeVec = self.ufes[op]()
            if op == self.rotate:
                # The runtimes for rotate return an MEulerRotation, which we
                # must convert to a vector in degrees, since updateTRS expects
                # it in that format.
                #
                r = runTimeVec.asVector()
                rtAll = self.updateTRS(
                    rtAll, op, [degrees(r[0]),
                                degrees(r[1]),
                                degrees(r[2])])
                r = ufeVec.asVector()
                ufeAll = self.updateTRS(
                    ufeAll, op, [degrees(r[0]),
                                 degrees(r[1]),
                                 degrees(r[2])])
            else:
                rtAll = self.updateTRS(rtAll, op, runTimeVec)
                ufeAll = self.updateTRS(ufeAll, op, ufeVec)
            assertVectorAlmostEqual(self, runTimeVec, ufeVec)

        assertVectorAlmostEqual(self, rtAll, ufeAll)

        return (rtAll, ufeAll)

    def runTestCombo(self, expectedTRS):
        '''Engine method to run move, rotate, and scale test.'''

        # Save the initial values to the memento list.
        self.snapShotAndTest(expectedTRS, 6)

        # Do a combination of commands, and compare with expected.
        # Note: scale not supported in kObject space, hence, no test
        #       rotate values are in degrees
        #
        ops = [[self.rotate, [10, 20, 30], om.MSpace.kObject],
               [self.move, [4, 5, 6], om.MSpace.kWorld],
               [self.move, [4, 5, 6], om.MSpace.kObject],
               [self.scale, [.1, 10, 100], om.MSpace.kObject],
               [self.rotate, [-10, -20, -30], om.MSpace.kWorld],
               [self.move, [-3, -2, -1], om.MSpace.kWorld],
               [self.scale, [1000, .01, .1], om.MSpace.kObject],
               [self.move, [-3, -2, -1], om.MSpace.kObject]]
        for item in ops:
            op = item[0]
            if (op not in self.ops):
                continue
            v = item[1]
            space = item[2]
            if (op == self.move):
                if (space == om.MSpace.kObject):
                    cmds.move(v[0],
                              v[1],
                              v[2],
                              relative=True,
                              os=True,
                              wd=True)
                else:
                    cmds.move(v[0], v[1], v[2], relative=True)
            elif (op == self.rotate):
                if (space == om.MSpace.kObject):
                    cmds.rotate(v[0],
                                v[1],
                                v[2],
                                relative=True,
                                os=True,
                                forceOrderXYZ=True)
                else:
                    cmds.rotate(v[0],
                                v[1],
                                v[2],
                                relative=True,
                                ws=True,
                                forceOrderXYZ=True)
            elif (op == self.scale):
                if (space == om.MSpace.kObject):
                    cmds.scale(v[0], v[1], v[2], relative=True)
                else:
                    # scale is only supported in object space; if it is
                    # eventually supported in world space, this would be the
                    # command to emit:
                    #cmds.scale(v[0], v[1], v[2], relative=True, ws=True)
                    # Fail if we attempt to test this type of operation
                    self.assertEqual(space, om.MSpace.kObject,
                                     'scale only supported in object space')
                    continue
            expectedTRS = self.updateTRS(expectedTRS, op, v, space)

            self.snapShotAndTest(expectedTRS, 6)

        # Test undo, redo.
        self.rewindMemento()
        self.fforwardMemento()

    def testComboMaya(self):
        '''Move, rotate, and scale Maya object, read through the Transform3d interface.'''
        # Give the sphere an initial position, rotation, scale, and select it.
        sphereObj = om.MSelectionList().add('pSphere1').getDagPath(0).node()
        sphereFn = om.MFnTransform(sphereObj)

        expectedTRS = None
        if (self.move in self.ops):
            expectedTRS = self.updateTRS(expectedTRS, self.move, [1, 2, 3])
            t = self.extractTRS(expectedTRS, self.move)
            sphereFn.setTranslation(om.MVector(t[0], t[1], t[2]),
                                    om.MSpace.kTransform)
        if (self.rotate in self.ops):
            expectedTRS = self.updateTRS(expectedTRS, self.rotate,
                                         [30, 60, 90])
            r = self.extractTRS(expectedTRS, self.rotate)
            sphereFn.setRotation(
                om.MEulerRotation(radians(r[0]), radians(r[1]), radians(r[2])),
                om.MSpace.kTransform)

        if (self.scale in self.ops):
            expectedTRS = self.updateTRS(expectedTRS, self.scale, [1, 2, 3])
            s = self.extractTRS(expectedTRS, self.scale)
            sphereFn.setScale(om.MVector(s[0], s[1], s[2]))

        spherePath = ufe.Path(mayaUtils.createUfePathSegment("|pSphere1"))
        sphereItem = ufe.Hierarchy.createItem(spherePath)

        ufe.GlobalSelection.get().append(sphereItem)

        # Create a Transform3d interface for it.
        transform3d = ufe.Transform3d.transform3d(sphereItem)

        # Set up the callables that will retrieve the translation.
        self.runTimes[self.move] = partial(sphereFn.translation,
                                           om.MSpace.kTransform)
        self.ufes[self.move] = partial(transform3dTranslation, transform3d)

        # Set up the callables that will retrieve the rotation.
        self.runTimes[self.rotate] = partial(sphereFn.rotation,
                                             om.MSpace.kTransform)
        self.ufes[self.rotate] = partial(transform3dRotation, transform3d)

        # Set up the callables that will retrieve the scale.
        self.runTimes[self.scale] = partial(sphereFn.scale)
        self.ufes[self.scale] = partial(transform3dScale, transform3d)

        self.runTestCombo(expectedTRS)

    def testComboUSD(self):
        '''Move, rotate, and scale USD object, read through the Transform3d interface.'''

        # Select Ball_35 to move, rotate, and scale it.
        ball35Path = ufe.Path([
            mayaUtils.createUfePathSegment("|transform1|proxyShape1"),
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")
        ])
        ball35Item = ufe.Hierarchy.createItem(ball35Path)

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

        # Create a Transform3d interface for it.
        transform3d = ufe.Transform3d.transform3d(ball35Item)

        # We compare the UFE ops with the USD run-time ops.  To
        # obtain the full ops of Ball_35, we need to add the USD
        # ops to the Maya proxy shape ops.
        proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath(
            0).node()
        proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj)

        def ball35Translation():
            ball35Prim = usdUtils.getPrimFromSceneItem(ball35Item)
            return addVec(proxyShapeXformFn.translation(om.MSpace.kTransform),
                          ball35Prim.GetAttribute('xformOp:translate').Get())

        def ball35Rotation():
            ball35Prim = usdUtils.getPrimFromSceneItem(ball35Item)
            if not ball35Prim.HasAttribute('xformOp:rotateXYZ'):
                return proxyShapeXformFn.rotation(om.MSpace.kTransform)
            else:
                x, y, z = ball35Prim.GetAttribute('xformOp:rotateXYZ').Get()
                return proxyShapeXformFn.rotation(
                    om.MSpace.kTransform) + om.MEulerRotation(
                        radians(x), radians(y), radians(z))

        def ball35Scale():
            ball35Prim = usdUtils.getPrimFromSceneItem(ball35Item)
            if not ball35Prim.HasAttribute('xformOp:scale'):
                return proxyShapeXformFn.scale()
            else:
                return combineScales(
                    proxyShapeXformFn.scale(),
                    ball35Prim.GetAttribute('xformOp:scale').Get())

        # Set up the callables that will retrieve the translation.
        self.runTimes[self.move] = ball35Translation
        self.ufes[self.move] = partial(transform3dTranslation, transform3d)

        # Set up the callables that will retrieve the rotation.
        self.runTimes[self.rotate] = ball35Rotation
        self.ufes[self.rotate] = partial(transform3dRotation, transform3d)

        # Set up the callables that will retrieve the scale.
        self.runTimes[self.scale] = ball35Scale
        self.ufes[self.scale] = partial(transform3dScale, transform3d)

        # Save the initial position to the memento list.
        expectedTRS = None
        if (self.move in self.ops):
            v = ball35Translation()
            expectedTRS = self.updateTRS(expectedTRS, self.move,
                                         [v[0], v[1], v[2]])
        if (self.rotate in self.ops):
            r = ball35Rotation().asVector()
            expectedTRS = self.updateTRS(
                expectedTRS, self.rotate,
                [degrees(r[0]), degrees(r[1]),
                 degrees(r[2])])

        if (self.scale in self.ops):
            s = ball35Scale()
            expectedTRS = self.updateTRS(expectedTRS, self.scale,
                                         [s[0], s[1], s[2]])

        self.runTestCombo(expectedTRS)

    @unittest.skipUnless(
        mayaUtils.mayaMajorVersion() >= 2022,
        'Rotate and scale pivot compensation only available in Maya 2022 or greater.'
    )
    def testRotateScalePivotCompensation(self):
        '''Test that rotate and scale pivot compensation match Maya object.'''

        cmds.file(new=True, force=True)
        mayaSphere = cmds.polySphere()[0]
        mayaSpherePath = ufe.PathString.path('|pSphere1')
        mayaSphereItem = ufe.Hierarchy.createItem(mayaSpherePath)

        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'])

        usdSpherePath = ufe.PathString.path('%s,/Sphere1' % proxyShape)
        usdSphereItem = ufe.Hierarchy.createItem(usdSpherePath)
        usdSphereT3d = ufe.Transform3d.transform3d(usdSphereItem)

        # If the Transform3d interface can't handle rotate or scale pivot
        # compensation, skip this test.
        if usdSphereT3d.translateRotatePivotCmd() is None or \
           usdSphereT3d.translateScalePivotCmd() is None:
            raise unittest.SkipTest(
                "Rotate or scale pivot compensation unsupported.")

        # Select both spheres.
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(mayaSphereItem)
        sn.append(usdSphereItem)

        # Rotate both spheres around X, and scale them.
        cmds.rotate(30, 0, 0, r=True, os=True, fo=True)
        cmds.scale(1, 1, 2, r=True)

        # Move pivots in world space.  At time of writing (20-Oct-20) UFE
        # rotate pivot and scale pivot arguments to move command doesn't accept
        # an object argument, so use the selection.
        cmds.move(0,
                  -2.104143,
                  3.139701,
                  "pSphere1.scalePivot",
                  "pSphere1.rotatePivot",
                  r=True)
        sn.remove(mayaSphereItem)
        cmds.move(0, -2.104143, 3.139701, r=True, urp=True, usp=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

        # Scale the spheres again
        sn.append(mayaSphereItem)
        cmds.scale(1, 1, 2, r=True)

        # Move the pivots again.
        cmds.move(0,
                  5.610465,
                  3.239203,
                  "pSphere1.scalePivot",
                  "pSphere1.rotatePivot",
                  r=True)
        sn.remove(mayaSphereItem)
        cmds.move(0, 5.610465, 3.239203, r=True, urp=True, usp=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

        # Move only the rotate pivot.
        cmds.move(0, 0, 3, r=True, urp=True)
        cmds.move(0, 0, 3, "pSphere1.rotatePivot", r=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

        # Move only the scale pivot.
        cmds.move(0, 0, -4, r=True, usp=True)
        cmds.move(0, 0, -4, "pSphere1.scalePivot", r=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

    @unittest.skipUnless(
        mayaUtils.mayaMajorVersion() >= 2022,
        'Rotate and scale pivot compensation only available in Maya 2022 or greater.'
    )
    def testRotateScalePivotCompensationAfterExport(self):
        '''Rotate and scale pivots must match after export.'''

        cmds.file(new=True, force=True)
        mayaSphere = cmds.polySphere()[0]

        cmds.rotate(0, 0, -45, r=True, os=True, fo=True)
        cmds.scale(4, 3, 2, r=True)
        cmds.move(-2, -3, -4, "pSphere1.rotatePivot", r=True)
        cmds.move(7, 6, 5, "pSphere1.scalePivot", r=True)

        # Export out, reference back in using proxy shape.
        usdFilePath = os.path.abspath('UsdExportMayaXformStack.usda')
        cmds.mayaUSDExport(file=usdFilePath)

        # Reference it back in.
        proxyShape = cmds.createNode('mayaUsdProxyShape')
        cmds.setAttr('mayaUsdProxyShape1.filePath', usdFilePath, type='string')

        # MAYA-101766: awkward plug access for non-interactive stage loading.
        outStageData = nameToPlug('mayaUsdProxyShape1.outStageData')
        outStageData.asMDataHandle()

        proxyShapeMayaPath = cmds.ls(proxyShape, long=True)[0]
        proxyShapePathSegment = mayaUtils.createUfePathSegment(
            proxyShapeMayaPath)

        spherePathSegment = usdUtils.createUfePathSegment('/pSphere1')
        spherePath = ufe.Path([proxyShapePathSegment, spherePathSegment])
        sphereItem = ufe.Hierarchy.createItem(spherePath)
        usdSphereT3d = ufe.Transform3d.transform3d(sphereItem)

        # If the Transform3d interface can't handle rotate or scale pivot
        # compensation, skip this test.
        if usdSphereT3d.translateRotatePivotCmd() is None or \
           usdSphereT3d.translateScalePivotCmd() is None:
            raise unittest.SkipTest(
                "Rotate or scale pivot compensation unsupported.")

        # Maya object and its exported USD object twin should have the
        # same pivots and pivot compensations.
        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

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

        # Move only the rotate pivot.
        cmds.move(-1, -2, -3, r=True, urp=True)
        cmds.move(-1, -2, -3, "pSphere1.rotatePivot", r=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

        # Move only the scale pivot.
        cmds.move(-4, -3, -2, r=True, usp=True)
        cmds.move(-4, -3, -2, "pSphere1.scalePivot", r=True)

        checkPivotsAndCompensations(self, "pSphere1", usdSphereT3d)

    @unittest.skipUnless(
        mayaUtils.mayaMajorVersion() >= 2022,
        'Fallback transform op handling only available in Maya 2022 or greater.'
    )
    def testFallbackCases(self):
        '''Fallback handler test cases.'''

        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.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")))

    @unittest.skipUnless(
        mayaUtils.previewReleaseVersion() >= 123,
        'Requires Maya fixes only available in Maya Preview Release 123 or later.'
    )
    def testBrokenFallback(self):
        '''Maya fallback transform stack must be final on prim transform op stack.'''
        # Create a prim and add transform ops to it that don't match the Maya
        # transform stack.  Then, transform it with Maya: this will trigger the
        # creation of a Maya fallback transform stack.  Finally, append another
        # transform op to the prim transform stack.  Because there is now a
        # transform op beyond the Maya fallback transform stack, the Maya
        # fallback has been "corrupted", and any further transformation of the
        # prim must be a no-op.

        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', 'Capsule'])

        capsulePath = ufe.PathString.path('%s,/Capsule1' % proxyShape)
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        capsulePrim = mayaUsd.ufe.ufePathToPrim(
            ufe.PathString.string(capsulePath))
        capsuleXformable = UsdGeom.Xformable(capsulePrim)
        capsuleXformable.AddRotateXOp()
        capsuleXformable.AddRotateYOp()

        self.assertEqual(capsuleXformable.GetXformOpOrderAttr().Get(),
                         Vt.TokenArray(("xformOp:rotateX", "xformOp:rotateY")))

        # Select capsule.
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(capsuleItem)

        # 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(
            capsuleXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(("xformOp:rotateX", "xformOp:rotateY",
                           "xformOp:rotateXYZ:maya_fallback")))

        capsuleT3d = ufe.Transform3d.transform3d(capsuleItem)
        self.assertIsNotNone(capsuleT3d)

        # Add another transform op to break the Maya fallback stack.
        capsuleXformable.AddRotateZOp()

        self.assertEqual(
            capsuleXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(
                ("xformOp:rotateX", "xformOp:rotateY",
                 "xformOp:rotateXYZ:maya_fallback", "xformOp:rotateZ")))

        # Do any transform editing with Maya.
        cmds.rotate(0, 0, 30, r=True, os=True, fo=True)

        capsuleT3d = ufe.Transform3d.transform3d(capsuleItem)
        self.assertIsNone(capsuleT3d)

    @unittest.skipIf(
        mayaUtils.previewReleaseVersion() < 123,
        'Fallback transform op handling only available in Maya Preview Release 123 or later.'
    )
    def testFallback(self):
        '''Transformable not handled by standard Transform3d handlers must be
    handled by fallback handler.'''

        mayaUtils.openTestScene("xformOpFallback", "fallbackTest.ma")

        # We have three objects in the scene, one Maya, one USD with a Maya
        # transform stack, and one USD which does not match any Transform3d
        # handler.  This last object is the one to which fallback transform ops
        # will be appended.
        mayaObj = '|null1|pSphere1'
        mayaSpherePath = ufe.PathString.path(mayaObj)
        usdSpherePath = ufe.PathString.path(
            '|fallbackTest|fallbackTestShape,/parent/sphere1')
        usdFallbackSpherePath = ufe.PathString.path(
            '|fallbackTest|fallbackTestShape,/sphere1')

        mayaSphereItem = ufe.Hierarchy.createItem(mayaSpherePath)
        usdSphereItem = ufe.Hierarchy.createItem(usdSpherePath)
        usdFallbackSphereItem = ufe.Hierarchy.createItem(usdFallbackSpherePath)
        # For scene items with fallback transform ops, the transform3d()
        # interface considers the complete object (i.e. all transform ops in
        # the stack), which is undesirable when setting and getting fallback
        # pivot transform ops.  To consider only the fallback transform ops,
        # use the editTransform3d() interface.  For scene items with only a
        # Maya transform stack, editTransform3d() and transform3d() are
        # equivalent, so arbitrarily choose editTransform3d().
        usdSphere3d = ufe.Transform3d.editTransform3d(
            usdSphereItem, ufe.EditTransform3dHint())
        usdFallbackSphere3d = ufe.Transform3d.editTransform3d(
            usdFallbackSphereItem, ufe.EditTransform3dHint())

        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(mayaSphereItem)
        sn.append(usdSphereItem)
        sn.append(usdFallbackSphereItem)

        # All objects should have the same world transform.  We use the Maya
        # world transform as the benchmark.
        checkWorldSpaceXform(self, [mayaObj, usdSphere3d, usdFallbackSphere3d])

        # Count the number of transform ops in the Maya transform stack sphere
        # and the fallback transform stack sphere.
        spherePrim = mayaUsd.ufe.ufePathToPrim(
            ufe.PathString.string(usdSpherePath))
        fallbackSpherePrim = mayaUsd.ufe.ufePathToPrim(
            ufe.PathString.string(usdFallbackSpherePath))
        sphereXformable = UsdGeom.Xformable(spherePrim)
        fallbackSphereXformable = UsdGeom.Xformable(fallbackSpherePrim)
        sphereOps = sphereXformable.GetOrderedXformOps()
        fallbackSphereOps = fallbackSphereXformable.GetOrderedXformOps()

        # Both prims have TRS transform ops.
        self.assertEqual(len(sphereOps), 3)
        self.assertEqual(len(fallbackSphereOps), 3)

        # First, translate all objects.
        cmds.move(0, 0, 5, r=True, os=True, wd=True)

        checkWorldSpaceXform(self, [mayaObj, usdSphere3d, usdFallbackSphere3d])

        # The sphere with the Maya transform stack has no additional transform
        # op; the sphere with the fallback stack will have an additional
        # translate op.
        sphereOps = sphereXformable.GetOrderedXformOps()
        fallbackSphereOps = fallbackSphereXformable.GetOrderedXformOps()

        self.assertEqual(len(sphereOps), 3)
        self.assertEqual(len(fallbackSphereOps), 4)

        # Rotate
        cmds.rotate(40, 0, 0, r=True, os=True, fo=True)

        checkWorldSpaceXform(self, [mayaObj, usdSphere3d, usdFallbackSphere3d])

        # The sphere with the Maya transform stack has no additional transform
        # op; the sphere with the fallback stack will have an additional
        # rotate op.
        sphereOps = sphereXformable.GetOrderedXformOps()
        fallbackSphereOps = fallbackSphereXformable.GetOrderedXformOps()

        self.assertEqual(len(sphereOps), 3)
        self.assertEqual(len(fallbackSphereOps), 5)

        # Scale
        cmds.scale(1, 1, 2.0, r=True)

        checkWorldSpaceXform(self, [mayaObj, usdSphere3d, usdFallbackSphere3d])

        # The sphere with the Maya transform stack has no additional transform
        # op; the sphere with the fallback stack will have an additional
        # scale op.
        sphereOps = sphereXformable.GetOrderedXformOps()
        fallbackSphereOps = fallbackSphereXformable.GetOrderedXformOps()

        self.assertEqual(len(sphereOps), 3)
        self.assertEqual(len(fallbackSphereOps), 6)

        # Command to change the pivots on Maya items and UFE items is
        # different, so remove Maya item from selection.
        sn.remove(mayaSphereItem)

        mayaPivots = [
            mayaObj + "." + attrName
            for attrName in ["scalePivot", "rotatePivot"]
        ]
        cmds.move(0, -2.5, 2.5, *mayaPivots, r=True)
        cmds.move(0, -2.5, 2.5, r=True, urp=True, usp=True)

        checkPivotsAndCompensations(self, mayaObj, usdSphere3d)
        checkPivotsAndCompensations(self, mayaObj, usdFallbackSphere3d)

        # Both spheres have 6 additional transform ops: rotate pivot and its
        # inverse, scale pivot and its inverse, rotate pivot translate, and
        # scale pivot translate.
        sphereOps = sphereXformable.GetOrderedXformOps()
        fallbackSphereOps = fallbackSphereXformable.GetOrderedXformOps()

        self.assertEqual(len(sphereOps), 9)
        self.assertEqual(len(fallbackSphereOps), 12)

        # Perform an additional pivot move, to ensure that the existing pivot
        # values are properly considered.
        cmds.move(0, -1, 1, *mayaPivots, r=True)
        cmds.move(0, -1, 1, r=True, urp=True, usp=True)

        checkPivotsAndCompensations(self, mayaObj, usdSphere3d)
        checkPivotsAndCompensations(self, mayaObj, usdFallbackSphere3d)

    @unittest.skipIf(
        int(cmds.about(apiVersion=True)) <= 20220000,
        'Center pivot command is only available in Maya 2022 or later.')
    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])

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testPrimPropertyPathNotifs only available in UFE v2 or greater.')
    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)
コード例 #6
0
 def __call__(self, notification):
     if (ufeUtils.ufeFeatureSetVersion() >= 2):
         if isinstance(notification, ufe.AttributeValueChanged):
             self._valueChanged += 1
     if isinstance(notification, ufe.Transform3dChanged):
         self._transform3d += 1
コード例 #7
0
    def testDelete(self):
        '''Delete Maya and USD objects.'''

        # Create our UFE notification observer
        ufeObs = TestObserver()

        if ufeUtils.ufeFeatureSetVersion() < 2:
            ufe.Scene.addObjectDeleteObserver(ufeObs)
            ufe.Scene.addObjectAddObserver(ufeObs)
        else:
            ufe.Scene.addObserver(ufeObs)

        # Select two objects, one Maya, one USD.
        spherePath = ufe.Path(mayaUtils.createUfePathSegment("|pSphere1"))
        sphereItem = ufe.Hierarchy.createItem(spherePath)
        sphereShapePath = ufe.Path(
            mayaUtils.createUfePathSegment("|pSphere1|pSphereShape1"))
        sphereShapeItem = ufe.Hierarchy.createItem(sphereShapePath)

        mayaSegment = mayaUtils.createUfePathSegment("|transform1|proxyShape1")
        ball35Path = ufe.Path([
            mayaSegment,
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")
        ])
        ball35Item = ufe.Hierarchy.createItem(ball35Path)
        propsPath = ufe.Path(
            [mayaSegment,
             usdUtils.createUfePathSegment("/Room_set/Props")])
        propsItem = ufe.Hierarchy.createItem(propsPath)

        sphereShapeName = str(sphereShapeItem.path().back())
        ball35Name = str(ball35Item.path().back())

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

        # Before delete, each item is a child of its parent.
        sphereHierarchy = ufe.Hierarchy.hierarchy(sphereItem)
        propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)

        ufeObs.reset()
        cmds.delete()

        # We deleted two items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 2)
        self.assertEqual(ufeObs.nbAddNotif(), 0)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)

        cmds.undo()

        # After the undo we added two items back.
        self.assertEqual(ufeObs.nbDeleteNotif(), 2)
        self.assertEqual(ufeObs.nbAddNotif(), 2)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertIn(sphereShapeItem, sphereChildren)
        self.assertIn(ball35Item, propsChildren)
        self.assertIn(sphereShapeName, sphereChildrenNames)
        self.assertIn(ball35Name, propsChildrenNames)

        cmds.redo()

        # After the redo we again deleted two items.
        self.assertEqual(ufeObs.nbDeleteNotif(), 4)
        self.assertEqual(ufeObs.nbAddNotif(), 2)

        sphereChildren = sphereHierarchy.children()
        propsChildren = propsHierarchy.children()

        sphereChildrenNames = childrenNames(sphereChildren)
        propsChildrenNames = childrenNames(propsChildren)

        self.assertNotIn(sphereShapeName, sphereChildrenNames)
        self.assertNotIn(ball35Name, propsChildrenNames)

        # undo to restore state to original.
        cmds.undo()

        # After the undo we again added two items back.
        self.assertEqual(ufeObs.nbDeleteNotif(), 4)
        self.assertEqual(ufeObs.nbAddNotif(), 4)
コード例 #8
0
class testLayerManagerSerialization(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls._inputPath = fixturesUtils.setUpClass(__file__)

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

    def setupEmptyScene(self):
        self._currentTestDir = tempfile.mkdtemp(prefix='LayerManagerTest')
        self._tempMayaFile = os.path.join(self._currentTestDir,
                                          'SerializationTest.ma')
        cmds.file(rename=self._tempMayaFile)

    def copyTestFilesAndMakeEdits(self):
        self._currentTestDir = tempfile.mkdtemp(prefix='LayerManagerTest')
        fromDirectory = os.path.join(self._inputPath,
                                     'LayerManagerSerializationTest')
        copy_tree(fromDirectory, self._currentTestDir)

        self._tempMayaFile = os.path.join(self._currentTestDir,
                                          'SerializationTest.ma')
        self._rootUsdFile = os.path.join(self._currentTestDir,
                                         'SerializationTest.usda')
        self._test1File = os.path.join(self._currentTestDir,
                                       'SerializationTest_1.usda')
        self._test1_1File = os.path.join(self._currentTestDir,
                                         'SerializationTest_1_1.usda')
        self._test2File = os.path.join(self._currentTestDir,
                                       'SerializationTest_2.usda')
        self._test2_1File = os.path.join(self._currentTestDir,
                                         'SerializationTest_2_1.usda')

        cmds.file(self._tempMayaFile, open=True, force=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        stage.SetEditTarget(stage.GetRootLayer())
        newPrimPath = "/ChangeInRoot"
        stage.DefinePrim(newPrimPath, "xform")

        stage.SetEditTarget(stack[2])
        newPrimPath = "/ChangeInLayer_1_1"
        stage.DefinePrim(newPrimPath, "xform")

        stage.SetEditTarget(stack[0])
        newPrimPath = "/ChangeInSessionLayer"
        stage.DefinePrim(newPrimPath, "xform")

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        cleanCount = 0
        dirtyCount = 0
        for l in stack:
            if l.dirty:
                dirtyCount += 1
            else:
                cleanCount += 1
        self.assertEqual(3, dirtyCount)
        self.assertEqual(3, cleanCount)

        return stage

    def confirmEditsSavedStatus(self, fileBackedSavedStatus,
                                sessionSavedStatus):
        cmds.file(new=True, force=True)

        proxyNode, stage = createProxyFromFile(self._rootUsdFile)

        newPrimPath = "/ChangeInRoot"
        self.assertEqual(fileBackedSavedStatus,
                         stage.GetPrimAtPath(newPrimPath).IsValid())

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertEqual(fileBackedSavedStatus,
                         stage.GetPrimAtPath(newPrimPath).IsValid())

        newPrimPath = "/ChangeInSessionLayer"
        self.assertEqual(sessionSavedStatus,
                         stage.GetPrimAtPath(newPrimPath).IsValid())

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testSaveAllToMaya is available only in UFE v2 or greater.')
    def testSaveAllToMaya(self):
        '''
        Verify that all USD edits are save into the Maya file.
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(False, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testSaveAllToUsd is available only in UFE v2 or greater.')
    def testSaveAllToUsd(self):
        '''
        Verify that all USD edits are saved back to the original .usd files
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(True, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testIgnoreAllUsd is available only in UFE v2 or greater.')
    def testIgnoreAllUsd(self):
        '''
        Verify that all USD edits are ignored
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(False, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testAnonymousRootToMaya is available only in UFE v2 or greater.')
    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)

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testAnonymousRootToMaya is available only in UFE v2 or greater.')
    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)
コード例 #9
0
class testProxyShapeBase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        inputPath = fixturesUtils.setUpClass(__file__)

        cls.mayaSceneFilePath = os.path.join(inputPath, 'ProxyShapeBaseTest',
                                             'ProxyShapeBase.ma')

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

    def testBoundingBox(self):
        cmds.file(self.mayaSceneFilePath, open=True, force=True)

        # Verify that the proxy shape read something from the USD file.
        bboxSize = cmds.getAttr('Cube_usd.boundingBoxSize')[0]
        self.assertEqual(bboxSize, (1.0, 1.0, 1.0))

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testDuplicateProxyStageAnonymous only available in UFE v2 or greater.'
    )
    def testDuplicateProxyStageAnonymous(self):
        '''
        Verify stage with new anonymous layer is duplicated properly.
        '''
        cmds.file(new=True, force=True)

        # create a proxy shape and add a Capsule prim
        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'])

        # proxy shape is expected to have one child
        proxyShapeHier = ufe.Hierarchy.hierarchy(proxyShapeItem)
        self.assertEqual(1, len(proxyShapeHier.children()))

        # child name is expected to be Capsule1
        self.assertEqual(str(proxyShapeHier.children()[0].nodeName()),
                         "Capsule1")

        # validate session name and anonymous tag name
        stage = mayaUsd.ufe.getStage(str(proxyShapePath))
        self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer())
        self.assertEqual(stage.GetEditTarget().GetLayer(),
                         stage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in stage.GetSessionLayer().identifier)
        self.assertEqual(True, "anonymousLayer1"
                         in stage.GetRootLayer().identifier)

        # add proxyShapeItem to selection list
        ufe.GlobalSelection.get().append(proxyShapeItem)

        # duplicate the proxyShape.
        cmds.duplicate()

        # get the next item in UFE selection list.
        snIter = iter(ufe.GlobalSelection.get())
        duplStageItem = next(snIter)

        # duplicated stage name is expected to be incremented correctly
        self.assertEqual(str(duplStageItem.nodeName()), "stage2")

        # duplicated proxyShape name is expected to be incremented correctly
        duplProxyShapeHier = ufe.Hierarchy.hierarchy(duplStageItem)
        duplProxyShapeItem = duplProxyShapeHier.children()[0]
        self.assertEqual(str(duplProxyShapeItem.nodeName()), "stageShape2")

        # duplicated ProxyShapeItem should have exactly one child
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))

        # child name is expected to be Capsule1
        childName = str(
            ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()
            [0].nodeName())
        self.assertEqual(childName, "Capsule1")

        # validate session name and anonymous tag name
        duplStage = mayaUsd.ufe.getStage(
            str(ufe.PathString.path('|stage2|stageShape2')))
        self.assertEqual(duplStage.GetLayerStack()[0],
                         duplStage.GetSessionLayer())
        self.assertEqual(duplStage.GetEditTarget().GetLayer(),
                         duplStage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in duplStage.GetSessionLayer().identifier)
        self.assertEqual(
            True, "anonymousLayer1" in duplStage.GetRootLayer().identifier)

        # confirm that edits are not shared (a.k.a divorced).
        cmds.delete('|stage2|stageShape2,/Capsule1')
        self.assertEqual(
            0, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(proxyShapeItem).children()))

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testDuplicateProxyStageFileBacked only available in UFE v2 or greater.'
    )
    def testDuplicateProxyStageFileBacked(self):
        '''
        Verify stage from file is duplicated properly.
        '''
        # open tree.ma scene
        mayaUtils.openTreeScene()

        # clear selection to start off
        cmds.select(clear=True)

        # select a USD object.
        mayaPathSegment = mayaUtils.createUfePathSegment(
            '|Tree_usd|Tree_usdShape')
        usdPathSegment = usdUtils.createUfePathSegment('/TreeBase')
        treebasePath = ufe.Path([mayaPathSegment])
        treebaseItem = ufe.Hierarchy.createItem(treebasePath)

        # TreeBase has two children
        self.assertEqual(1,
                         len(ufe.Hierarchy.hierarchy(treebaseItem).children()))

        # get the USD stage
        stage = mayaUsd.ufe.getStage(str(mayaPathSegment))

        # by default edit target is set to the Rootlayer.
        self.assertEqual(stage.GetEditTarget().GetLayer(),
                         stage.GetRootLayer())

        # add treebaseItem to selection list
        ufe.GlobalSelection.get().append(treebaseItem)

        # duplicate
        cmds.duplicate()

        # get the next item in UFE selection list.
        snIter = iter(ufe.GlobalSelection.get())
        duplStageItem = next(snIter)

        # duplicated stage name is expected to be incremented correctly
        self.assertEqual(str(duplStageItem.nodeName()), "Tree_usd1")

        # duplicated proxyShape name is expected to be incremented correctly
        duplProxyShapeHier = ufe.Hierarchy.hierarchy(duplStageItem)
        duplProxyShapeItem = duplProxyShapeHier.children()[0]
        self.assertEqual(str(duplProxyShapeItem.nodeName()), "Tree_usd1Shape")

        # duplicated ProxyShapeItem should have exactly one child
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))

        # child name is expected to be Capsule1
        childName = str(
            ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()
            [0].nodeName())
        self.assertEqual(childName, "TreeBase")

        # validate session name and anonymous tag name
        duplStage = mayaUsd.ufe.getStage(
            str(ufe.PathString.path('|Tree_usd1|Tree_usd1Shape')))
        self.assertEqual(duplStage.GetLayerStack()[0],
                         duplStage.GetSessionLayer())
        self.assertEqual(duplStage.GetEditTarget().GetLayer(),
                         duplStage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in duplStage.GetSessionLayer().identifier)
        self.assertEqual(True, "tree.usd"
                         in duplStage.GetRootLayer().identifier)

        # delete "/TreeBase"
        cmds.delete('|Tree_usd1|Tree_usd1Shape,/TreeBase')

        # confirm that edits are shared. Both source and duplicated proxyShapes have no children now.
        self.assertEqual(
            0, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))
        self.assertEqual(0,
                         len(ufe.Hierarchy.hierarchy(treebaseItem).children()))
コード例 #10
0
class SelectTestCase(unittest.TestCase):
    '''Verify UFE selection on a USD scene.'''

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

    @classmethod
    def tearDownClass(cls):
        cmds.file(new=True, force=True)

    def setUp(self):
        # Load plugins
        self.assertTrue(self.pluginsLoaded)

        # Load a file that has the same scene in both the Maya Dag
        # hierarchy and the USD hierarchy.
        mayaUtils.openTestScene("parentCmd", "simpleSceneMayaPlusUSD_TRS.ma")

        # Create multiple scene items.  We will alternate between selecting a
        # Maya item and a USD item, 3 items each data model, one item at a
        # time, so we select 6 different items, one at a time.
        shapeSegment = mayaUtils.createUfePathSegment(
            "|mayaUsdProxy1|mayaUsdProxyShape1")
        ufeNames = ["cubeXform", "cylinderXform", "sphereXform"]
        mayaNames = ["pCube1", "pCylinder1", "pSphere1"]
        usdPaths = []
        for n in ["/" + o for o in ufeNames]:
            usdPaths.append(
                ufe.Path([shapeSegment,
                          usdUtils.createUfePathSegment(n)]))
        mayaPaths = []
        for n in ["|" + o for o in mayaNames]:
            mayaPaths.append(ufe.Path(mayaUtils.createUfePathSegment(n)))

        # Create a list of paths by alternating USD objects and Maya objects
        # Flatten zipped tuples using list comprehension double loop.
        zipped = zip(usdPaths, mayaPaths)
        paths = [j for i in zipped for j in i]

        # Create items for all paths.
        self.items = [ufe.Hierarchy.createItem(p) for p in paths]

        # Clear selection to start off
        cmds.select(clear=True)

    def runTestSelection(self, selectCmd):
        '''Run the replace selection test, using the argument command to
        replace the selection with a single scene item.'''

        # Clear the selection.
        globalSn = ufe.GlobalSelection.get()
        globalSn.clear()
        self.assertTrue(globalSn.empty())

        # Select all items in turn.
        for item in self.items:
            selectCmd(item)

        # Item in the selection should be the last item in our list.
        self.assertEqual(len(globalSn), 1)

        def snFront(sn):
            return next(iter(sn)) if \
                ufe.VersionInfo.getMajorVersion() == 1 else sn.front()

        self.assertEqual(snFront(globalSn), self.items[-1])

        # Check undo.  For this purpose, re-create the list of items in reverse
        # order.  Because we're already at the last item, we skip the last one
        # (i.e. last item is -1, so start at -2, and increment by -1).
        rItems = self.items[-2::-1]

        # Undo until the first element, checking the selection as we go.
        for i in rItems:
            cmds.undo()
            self.assertEqual(len(globalSn), 1)
            self.assertEqual(snFront(globalSn), i)

        # Check redo.
        fItems = self.items[1:]

        # Redo until the last element, checking the selection as we go.
        for i in fItems:
            cmds.redo()
            self.assertEqual(len(globalSn), 1)
            self.assertEqual(snFront(globalSn), i)

    def testUfeSelect(self):
        def selectCmd(item):
            sn = ufe.Selection()
            sn.append(item)
            ufeSelectCmd.replaceWith(sn)

        self.runTestSelection(selectCmd)

    @unittest.skipUnless((
        (ufeUtils.ufeFeatureSetVersion() >= 2) and
        (mayaUtils.previewReleaseVersion() >= 121)
    ), 'testMayaSelect only available in UFE v2 or greater and Maya Preview Release 121 or later.'
                         )
    def testMayaSelect(self):
        # Maya PR 121 now has support for UFE path string in select command.
        def selectCmd(item):
            cmds.select(ufe.PathString.string(item.path()))

        self.runTestSelection(selectCmd)

    @unittest.skipUnless((
        (ufeUtils.ufeFeatureSetVersion() >= 2) and
        (mayaUtils.previewReleaseVersion() >= 121)
    ), 'testMayaSelectFlags only available in UFE v2 or greater and Maya Preview Release 121 or later.'
                         )
    def testMayaSelectFlags(self):
        # Maya PR 121 now has support for UFE path string in select command.

        # Clear the selection.
        globalSn = ufe.GlobalSelection.get()
        globalSn.clear()
        self.assertTrue(globalSn.empty())

        # Incrementally add to the selection all items in turn.
        # Also testing undo/redo along the way.
        cnt = 0
        for item in self.items:
            cnt += 1
            cmds.select(ufe.PathString.string(item.path()), add=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt - 1, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

        # Since we added all the items to the global selection, it
        # should have all of them.
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        # Incrementally remove from the selection all items in turn.
        # Also testing undo/redo along the way.
        for item in self.items:
            cnt -= 1
            cmds.select(ufe.PathString.string(item.path()), deselect=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt + 1, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

        # Since we removed all items from global selection it
        # should be empty now.
        self.assertTrue(globalSn.empty())

        # Incrementally toggle selection state of all items in turn.
        # Since they all start unselected, they will toggle to selected.
        # Also testing undo/redo along the way.
        globalSn.clear()
        self.assertTrue(globalSn.empty())
        cnt = 0
        for item in self.items:
            cnt += 1
            cmds.select(ufe.PathString.string(item.path()), toggle=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt - 1, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

        # Since we toggled each item to selected, we should have all
        # of them on the global selection.
        self.assertEqual(len(globalSn), len(self.items))

        # Incrementally toggle selection state of all items in turn.
        # Since they all start selected, they will toggle to unselected.
        # Also testing undo/redo along the way.
        for item in self.items:
            cnt -= 1
            cmds.select(ufe.PathString.string(item.path()), toggle=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt + 1, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

        # Since we toggled all items to unselected, the global selection
        # should be empty now.
        self.assertTrue(globalSn.empty())

        # Select all the items at once, replacing the existing selection.
        # Also testing undo/redo.
        pathStrings = [ufe.PathString.string(i.path()) for i in self.items]
        cmds.select(*pathStrings, replace=True)
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        cmds.undo()
        self.assertEqual(0, len(globalSn))
        self.assertTrue(globalSn.empty())

        cmds.redo()
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        # With all items selected (and in same order as item order)
        # "select -add" the first item which will move it to the end.
        first = self.items[0]
        self.assertEqual(globalSn.front(), first)
        cmds.select(ufe.PathString.string(first.path()), add=True)
        self.assertTrue(globalSn.contains(first.path()))
        self.assertEqual(globalSn.back(), first)
コード例 #11
0
class testProxyShapeBase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        inputPath = fixturesUtils.setUpClass(__file__)

        cls.mayaSceneFilePath = os.path.join(inputPath, 'ProxyShapeBaseTest',
                                             'ProxyShapeBase.ma')

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

    def testBoundingBox(self):
        cmds.file(self.mayaSceneFilePath, open=True, force=True)

        # Verify that the proxy shape read something from the USD file.
        bboxSize = cmds.getAttr('Cube_usd.boundingBoxSize')[0]
        self.assertEqual(bboxSize, (1.0, 1.0, 1.0))

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testDuplicateProxyStageAnonymous only available in UFE v2 or greater.'
    )
    def testDuplicateProxyStageAnonymous(self):
        '''
        Verify stage with new anonymous layer is duplicated properly.
        '''
        cmds.file(new=True, force=True)

        # create a proxy shape and add a Capsule prim
        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'])

        # proxy shape is expected to have one child
        proxyShapeHier = ufe.Hierarchy.hierarchy(proxyShapeItem)
        self.assertEqual(1, len(proxyShapeHier.children()))

        # child name is expected to be Capsule1
        self.assertEqual(str(proxyShapeHier.children()[0].nodeName()),
                         "Capsule1")

        # validate session name and anonymous tag name
        stage = mayaUsd.ufe.getStage(str(proxyShapePath))
        self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer())
        self.assertEqual(stage.GetEditTarget().GetLayer(),
                         stage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in stage.GetSessionLayer().identifier)
        self.assertEqual(True, "anonymousLayer1"
                         in stage.GetRootLayer().identifier)

        # add proxyShapeItem to selection list
        ufe.GlobalSelection.get().append(proxyShapeItem)

        # duplicate the proxyShape.
        cmds.duplicate()

        # get the next item in UFE selection list.
        snIter = iter(ufe.GlobalSelection.get())
        duplStageItem = next(snIter)

        # duplicated stage name is expected to be incremented correctly
        self.assertEqual(str(duplStageItem.nodeName()), "stage2")

        # duplicated proxyShape name is expected to be incremented correctly
        duplProxyShapeHier = ufe.Hierarchy.hierarchy(duplStageItem)
        duplProxyShapeItem = duplProxyShapeHier.children()[0]
        self.assertEqual(str(duplProxyShapeItem.nodeName()), "stageShape2")

        # duplicated ProxyShapeItem should have exactly one child
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))

        # child name is expected to be Capsule1
        childName = str(
            ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()
            [0].nodeName())
        self.assertEqual(childName, "Capsule1")

        # validate session name and anonymous tag name
        duplStage = mayaUsd.ufe.getStage(
            str(ufe.PathString.path('|stage2|stageShape2')))
        self.assertEqual(duplStage.GetLayerStack()[0],
                         duplStage.GetSessionLayer())
        self.assertEqual(duplStage.GetEditTarget().GetLayer(),
                         duplStage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in duplStage.GetSessionLayer().identifier)
        self.assertEqual(
            True, "anonymousLayer1" in duplStage.GetRootLayer().identifier)

        # confirm that edits are not shared (a.k.a divorced).
        cmds.delete('|stage2|stageShape2,/Capsule1')
        self.assertEqual(
            0, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(proxyShapeItem).children()))

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testDuplicateProxyStageFileBacked only available in UFE v2 or greater.'
    )
    def testDuplicateProxyStageFileBacked(self):
        '''
        Verify stage from file is duplicated properly.
        '''
        # open tree.ma scene
        mayaUtils.openTreeScene()

        # clear selection to start off
        cmds.select(clear=True)

        # select a USD object.
        mayaPathSegment = mayaUtils.createUfePathSegment(
            '|Tree_usd|Tree_usdShape')
        usdPathSegment = usdUtils.createUfePathSegment('/TreeBase')
        treebasePath = ufe.Path([mayaPathSegment])
        treebaseItem = ufe.Hierarchy.createItem(treebasePath)

        # TreeBase has two children
        self.assertEqual(1,
                         len(ufe.Hierarchy.hierarchy(treebaseItem).children()))

        # get the USD stage
        stage = mayaUsd.ufe.getStage(str(mayaPathSegment))

        # by default edit target is set to the Rootlayer.
        self.assertEqual(stage.GetEditTarget().GetLayer(),
                         stage.GetRootLayer())

        # add treebaseItem to selection list
        ufe.GlobalSelection.get().append(treebaseItem)

        # duplicate
        cmds.duplicate()

        # get the next item in UFE selection list.
        snIter = iter(ufe.GlobalSelection.get())
        duplStageItem = next(snIter)

        # duplicated stage name is expected to be incremented correctly
        self.assertEqual(str(duplStageItem.nodeName()), "Tree_usd1")

        # duplicated proxyShape name is expected to be incremented correctly
        duplProxyShapeHier = ufe.Hierarchy.hierarchy(duplStageItem)
        duplProxyShapeItem = duplProxyShapeHier.children()[0]
        self.assertEqual(str(duplProxyShapeItem.nodeName()), "Tree_usd1Shape")

        # duplicated ProxyShapeItem should have exactly one child
        self.assertEqual(
            1, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))

        # child name is expected to be Capsule1
        childName = str(
            ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()
            [0].nodeName())
        self.assertEqual(childName, "TreeBase")

        # validate session name and anonymous tag name
        duplStage = mayaUsd.ufe.getStage(
            str(ufe.PathString.path('|Tree_usd1|Tree_usd1Shape')))
        self.assertEqual(duplStage.GetLayerStack()[0],
                         duplStage.GetSessionLayer())
        self.assertEqual(duplStage.GetEditTarget().GetLayer(),
                         duplStage.GetRootLayer())
        self.assertEqual(True, "-session"
                         in duplStage.GetSessionLayer().identifier)
        self.assertEqual(True, "tree.usd"
                         in duplStage.GetRootLayer().identifier)

        # delete "/TreeBase"
        cmds.delete('|Tree_usd1|Tree_usd1Shape,/TreeBase')

        # confirm that edits are shared. Both source and duplicated proxyShapes have no children now.
        self.assertEqual(
            0, len(ufe.Hierarchy.hierarchy(duplProxyShapeItem).children()))
        self.assertEqual(0,
                         len(ufe.Hierarchy.hierarchy(treebaseItem).children()))

    def testShareStage(self):
        '''
        Verify share/unshare stage workflow works properly
        '''
        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootIdentifier = stage.GetRootLayer().identifier

        # check that the stage is shared and the root is the right one
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().GetDisplayName(),
                         "cylinder.usda")

        # unshare the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), False)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # check that the stage is now unshared and the root is the anon layer
        # and the old root is now sublayered under that
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(rootLayer.GetDisplayName(), "unshareableLayer")
        self.assertEqual(rootLayer.subLayerPaths, [rootIdentifier])

        # re-share the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), True)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # check that the stage is now shared again and the layer is the same
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().GetDisplayName(),
                         "cylinder.usda")

    def testSerializationShareStage(self):
        '''
        Verify share/unshare stage works with serialization and complex heirharchies
        '''
        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        originalRootIdentifier = stage.GetRootLayer().identifier

        # check that the stage is shared and the root is the right one
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().GetDisplayName(),
                         "cylinder.usda")

        # unshare the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), False)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # check that the stage is now unshared and the root is the anon layer
        # and the old root is now sublayered under that
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(rootLayer.GetDisplayName(), "unshareableLayer")
        self.assertEqual(rootLayer.subLayerPaths, [originalRootIdentifier])

        middleLayer = Sdf.Layer.CreateAnonymous("middleLayer")
        middleLayer.subLayerPaths = [originalRootIdentifier]
        rootLayer.subLayerPaths = [middleLayer.identifier]

        # Save and re-open
        testDir = tempfile.mkdtemp(prefix='ProxyShapeBase')
        tempMayaFile = os.path.join(testDir, 'ShareStageSerializationTest.ma')
        cmds.file(rename=tempMayaFile)
        # make the USD layer absolute otherwise it won't be found
        cmds.setAttr('{}.{}'.format(proxyShapePath, "filePath"),
                     originalRootIdentifier,
                     type='string')
        cmds.file(save=True, force=True)
        cmds.file(new=True, force=True)
        cmds.file(tempMayaFile, open=True)

        # get the stage again (since we opened a new file)
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # make sure the middle layer is back (only one)
        self.assertEqual(len(rootLayer.subLayerPaths), 1)
        middleLayer = Sdf.Layer.Find(rootLayer.subLayerPaths[0])
        self.assertEqual(middleLayer.GetDisplayName(), "middleLayer")

        # make sure the middle layer still contains the original root only
        self.assertEqual(len(middleLayer.subLayerPaths), 1)
        self.assertEqual(middleLayer.subLayerPaths[0], originalRootIdentifier)

        # re-share the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), True)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # check that the stage is now shared again and the identifier is correct
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().identifier,
                         originalRootIdentifier)

    def testShareStageComplexHierarchyToggle(self):
        '''
        Verify share/unshare stage toggle works with complex heirharchies
        '''
        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        originalRootIdentifier = stage.GetRootLayer().identifier

        # check that the stage is shared and the root is the right one
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().GetDisplayName(),
                         "cylinder.usda")

        # unshare the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), False)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # check that the stage is now unshared and the root is the anon layer
        # and the old root is now sublayered under that
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(rootLayer.GetDisplayName(), "unshareableLayer")
        self.assertEqual(rootLayer.subLayerPaths, [originalRootIdentifier])

        middleLayer = Sdf.Layer.CreateAnonymous("middleLayer")
        middleLayer.subLayerPaths = [originalRootIdentifier]
        rootLayer.subLayerPaths = [middleLayer.identifier]

        # Save and re-open
        testDir = tempfile.mkdtemp(prefix='ProxyShapeBase')
        tempMayaFile = os.path.join(testDir,
                                    'ShareStageComplexHierarchyToggle.ma')
        cmds.file(rename=tempMayaFile)
        # make the USD layer absolute otherwise it won't be found
        cmds.setAttr('{}.{}'.format(proxyShapePath, "filePath"),
                     originalRootIdentifier,
                     type='string')
        cmds.file(save=True, force=True)
        cmds.file(new=True, force=True)
        cmds.file(tempMayaFile, open=True)

        # get the stage again (since we opened a new file)
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # make sure the middle layer is back (only one)
        self.assertEqual(len(rootLayer.subLayerPaths), 1)
        middleLayer = Sdf.Layer.Find(rootLayer.subLayerPaths[0])
        self.assertEqual(middleLayer.GetDisplayName(), "middleLayer")

        # re-share the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), True)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # check that the stage is now shared again and the identifier is correct
        self.assertTrue(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(stage.GetRootLayer().identifier,
                         originalRootIdentifier)

        # unshare the stage
        cmds.setAttr('{}.{}'.format(proxyShapePath, "shareStage"), False)
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()
        rootLayer = stage.GetRootLayer()

        # check that the stage is now shared and the root is the anon layer
        # and the old root is now sublayered under that
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapePath, "shareStage")))
        self.assertEqual(rootLayer.GetDisplayName(), "unshareableLayer")
        self.assertEqual(rootLayer.subLayerPaths, [middleLayer.identifier])

    def testShareStageSourceChange(self):
        '''
        Verify the stage source change maintains the position in the hierarchy
        '''
        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the proxy shape path
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapeA = proxyShapes[0]

        # create another proxyshape (B)
        import mayaUsd_createStageWithNewLayer
        proxyShapeB = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapeB = proxyShapeB.split("|")[-1]

        # Connect them using stage data
        cmds.connectAttr('{}.outStageData'.format(proxyShapeA),
                         '{}.inStageData'.format(proxyShapeB))

        # get the stage
        stageB = mayaUsd.lib.GetPrim(proxyShapeB).GetStage()
        originalRootIdentifierB = stageB.GetRootLayer().identifier

        # check that the stage is shared and the root is the right one
        self.assertTrue(cmds.getAttr('{}.{}'.format(proxyShapeB,
                                                    "shareStage")))
        self.assertEqual(stageB.GetRootLayer().GetDisplayName(),
                         "cylinder.usda")

        # unshare the stage
        cmds.setAttr('{}.{}'.format(proxyShapeB, "shareStage"), False)
        stageB = mayaUsd.lib.GetPrim(proxyShapeB).GetStage()
        rootLayerB = stageB.GetRootLayer()

        # check that the stage is now unshared and the root is the anon layer
        # and the old root is now sublayered under that
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapeB, "shareStage")))
        self.assertEqual(rootLayerB.GetDisplayName(), "unshareableLayer")
        self.assertEqual(rootLayerB.subLayerPaths, [originalRootIdentifierB])

        # Add complex hierarchy
        middleLayerB = Sdf.Layer.CreateAnonymous("middleLayer")
        middleLayerB.subLayerPaths = [originalRootIdentifierB]
        rootLayerB.subLayerPaths = [middleLayerB.identifier]

        # unshare the stage from the first proxy
        cmds.setAttr('{}.{}'.format(proxyShapeA, "shareStage"), False)
        stageA = mayaUsd.lib.GetPrim(proxyShapeA).GetStage()
        rootLayerA = stageA.GetRootLayer()
        stageB = mayaUsd.lib.GetPrim(proxyShapeB).GetStage()
        rootLayerB = stageB.GetRootLayer()

        # check that the stage is now unshared and the entire hierachy is good (A)
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapeA, "shareStage")))
        self.assertEqual(rootLayerA.GetDisplayName(), "unshareableLayer")
        self.assertEqual(len(rootLayerA.subLayerPaths), 1)
        self.assertEqual(rootLayerA.subLayerPaths[0], originalRootIdentifierB)

        # Make sure the hierachy is good (B)
        self.assertFalse(
            cmds.getAttr('{}.{}'.format(proxyShapeB, "shareStage")))
        self.assertEqual(rootLayerB.GetDisplayName(), "unshareableLayer")
        self.assertEqual(len(rootLayerB.subLayerPaths), 1)
        middleLayer = Sdf.Layer.Find(rootLayerB.subLayerPaths[0])
        self.assertEqual(middleLayer.GetDisplayName(), "middleLayer")
        self.assertEqual(len(middleLayer.subLayerPaths), 1)
        unshareableLayerFromA = Sdf.Layer.Find(middleLayer.subLayerPaths[0])
        self.assertEqual(unshareableLayerFromA.GetDisplayName(),
                         "unshareableLayer")
        self.assertEqual(len(unshareableLayerFromA.subLayerPaths), 1)
        self.assertEqual(unshareableLayerFromA.subLayerPaths[0],
                         originalRootIdentifierB)
コード例 #12
0
ファイル: testObject3d.py プロジェクト: nirmal232/maya-usd
class Object3dTestCase(unittest.TestCase):
    '''Verify the Object3d UFE interface, for the USD runtime.
    '''

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)

        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

    def setUp(self):
        ''' Called initially to set up the Maya test environment '''
        self.assertTrue(self.pluginsLoaded)

    def testBoundingBox(self):
        '''Test the Object3d bounding box interface.'''

        # Create a simple USD scene.  Default sphere radius is 1, so extents
        # are from (-1, -1, -1) to (1, 1, 1).
        usdFilePath = cmds.internalVar(utd=1) + '/testObject3d.usda'
        stage = Usd.Stage.CreateNew(usdFilePath)
        xform = stage.DefinePrim('/parent', 'Xform')
        sphere = stage.DefinePrim('/parent/sphere', 'Sphere')
        extentAttr = sphere.GetAttribute('extent')
        extent = extentAttr.Get()
        
        assertVectorAlmostEqual(self, extent[0], [-1]*3)
        assertVectorAlmostEqual(self, extent[1], [1]*3)

        # Move the sphere.  Its UFE bounding box should not be affected by
        # transformation hierarchy.
        UsdGeom.XformCommonAPI(xform).SetTranslate((7, 8, 9))

        # Save out the file, and bring it back into Maya under a proxy shape.
        stage.GetRootLayer().Save()

        proxyShape = cmds.createNode('mayaUsdProxyShape')
        cmds.setAttr('mayaUsdProxyShape1.filePath', usdFilePath, type='string')

        # MAYA-101766: loading a stage is done by the proxy shape compute.
        # Because we're a script, no redraw is done, so need to pull explicitly
        # on the outStageData (and simply discard the returned data), to get
        # the proxy shape compute to run, and thus the stage to load.  This
        # should be improved.  Without a loaded stage, UFE item creation
        # fails.  PPT, 31-10-2019.
        outStageData = nameToPlug('mayaUsdProxyShape1.outStageData')
        outStageData.asMDataHandle()

        proxyShapeMayaPath = cmds.ls(proxyShape, long=True)[0]
        proxyShapePathSegment = mayaUtils.createUfePathSegment(
            proxyShapeMayaPath)
        
        #######
        # Create a UFE scene item from the sphere prim.
        spherePathSegment = usdUtils.createUfePathSegment('/parent/sphere')
        spherePath = ufe.Path([proxyShapePathSegment, spherePathSegment])
        sphereItem = ufe.Hierarchy.createItem(spherePath)

        # Get its Object3d interface.
        object3d = ufe.Object3d.object3d(sphereItem)

        # Get its bounding box.
        ufeBBox = object3d.boundingBox()

        # Compare it to known extents.
        assertVectorAlmostEqual(self, ufeBBox.min.vector, [-1]*3)
        assertVectorAlmostEqual(self, ufeBBox.max.vector, [1]*3)

        #######
        # Create a UFE scene item from the parent Xform of the sphere prim.
        parentPathSegment = usdUtils.createUfePathSegment('/parent')
        parentPath = ufe.Path([proxyShapePathSegment, parentPathSegment])
        parentItem = ufe.Hierarchy.createItem(parentPath)

        # Get its Object3d interface.
        parentObject3d = ufe.Object3d.object3d(parentItem)

        # Get its bounding box.
        parentUFEBBox = parentObject3d.boundingBox()

        # Compare it to sphere's extents.
        assertVectorEqual(self, ufeBBox.min.vector, parentUFEBBox.min.vector)
        assertVectorEqual(self, ufeBBox.max.vector, parentUFEBBox.max.vector)


        #######
        # Remove the test file.
        os.remove(usdFilePath)

    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)

    def testAnimatedBoundingBox(self):
        '''Test the Object3d bounding box interface for animated geometry.'''

        # Open sphereAnimatedRadiusProxyShape.ma scene in testSamples
        mayaUtils.openSphereAnimatedRadiusScene()

        # The extents of the sphere are copied from the .usda file.
        expected = [
            [(-1.0000002, -1, -1.0000005), (1, 1, 1.0000001)],
            [(-1.3086424, -1.308642, -1.3086426), (1.308642, 1.308642, 1.3086421)],
            [(-2.135803, -2.1358025, -2.1358035), (2.1358025, 2.1358025, 2.1358027)],
            [(-3.333334, -3.3333333, -3.333335), (3.3333333, 3.3333333, 3.3333337)],
            [(-4.7530875, -4.7530866, -4.753089), (4.7530866, 4.7530866, 4.753087)],
            [(-6.246915, -6.2469134, -6.2469163), (6.2469134, 6.2469134, 6.2469144)],
            [(-7.6666684, -7.6666665, -7.6666703), (7.6666665, 7.6666665, 7.6666675)],
            [(-8.8642, -8.864198, -8.864202), (8.864198, 8.864198, 8.864199)],
            [(-9.6913595, -9.691358, -9.691362), (9.691358, 9.691358, 9.691359)],
            [(-10.000002, -10, -10.000005), (10, 10, 10.000001)]]

        # Create an Object3d interface for USD sphere.
        mayaPathSegment = mayaUtils.createUfePathSegment(
            '|transform1|proxyShape1')
        usdPathSegment = usdUtils.createUfePathSegment('/pSphere1')

        spherePath = ufe.Path([mayaPathSegment, usdPathSegment])
        sphereItem = ufe.Hierarchy.createItem(spherePath)

        object3d = ufe.Object3d.object3d(sphereItem)

        # Loop over frames 1 to 10, and compare the values returned to the
        # expected values.
        for frame in range(1,11):
            cmds.currentTime(frame)

            ufeBBox = object3d.boundingBox()

            # Compare it to known extents.
            assertVectorAlmostEqual(self, ufeBBox.min.vector,
                                    expected[frame-1][0], places=6)
            assertVectorAlmostEqual(self, ufeBBox.max.vector,
                                    expected[frame-1][1], places=6)

    def testVisibility(self):
        '''Test the Object3d visibility methods.'''

        # Open top_layer.ma scene in testSamples
        mayaUtils.openTopLayerScene()

        # Get a scene item for Ball_35.
        ball35Path = ufe.Path([
            mayaUtils.createUfePathSegment("|transform1|proxyShape1"), 
            usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")])
        ball35Item = ufe.Hierarchy.createItem(ball35Path)

        # Create an Object3d interface for it.
        object3d = ufe.Object3d.object3d(ball35Item)

        visObs = TestObserver()

        # We start off with no visibility observers.
        self.assertFalse(ufe.Object3d.hasObserver(visObs))
        self.assertEqual(ufe.Object3d.nbObservers(), 0)

        # Set the observer for visibility changes.
        ufe.Object3d.addObserver(visObs)
        self.assertTrue(ufe.Object3d.hasObserver(visObs))
        self.assertEqual(ufe.Object3d.nbObservers(), 1)

        # No notifications yet.
        self.assertEqual(visObs.notifications(), 0)

        # Initially it should be visible.
        self.assertTrue(object3d.visibility())

        # Make it invisible.
        object3d.setVisibility(False)
        self.assertFalse(object3d.visibility())

        # We should have got 'one' notification.
        self.assertEqual(visObs.notifications(), 1)

        # Make it visible.
        object3d.setVisibility(True)
        self.assertTrue(object3d.visibility())

        # We should have got one more notification.
        self.assertEqual(visObs.notifications(), 2)

        # Remove the observer.
        ufe.Object3d.removeObserver(visObs)
        self.assertFalse(ufe.Object3d.hasObserver(visObs))
        self.assertEqual(ufe.Object3d.nbObservers(), 0)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testUndoVisibleCmd only available in UFE v2 or greater.')
    def testUndoVisibleCmd(self):

        ''' Verify the token / attribute values for visibility after performing undo/redo '''

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

        # create a Capsule via contextOps menu
        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'])

        # create an Object3d interface.
        capsulePath = ufe.PathString.path('%s,/Capsule1' % proxyShape)
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        capsulePrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(capsulePath))
        object3d = ufe.Object3d.object3d(capsuleItem)

        # stage / primSpec
        stage = mayaUsd.ufe.getStage(str(proxyShapePath))
        primSpec = stage.GetEditTarget().GetPrimSpecForScenePath('/Capsule1');

        # initially capsuleItem should be visible.
        self.assertTrue(object3d.visibility())

        # make it invisible.
        visibleCmd = object3d.setVisibleCmd(False)
        visibleCmd.execute()

        # get the visibility "attribute"
        visibleAttr = capsulePrim.GetAttribute('visibility')

        # expect the visibility attribute to be 'invisible'
        self.assertEqual(visibleAttr.Get(), 'invisible')

        # visibility "token" must exists now in the USD data model
        self.assertTrue(bool(primSpec and UsdGeom.Tokens.visibility in primSpec.attributes))

        # undo
        visibleCmd.undo()

        # expect the visibility attribute to be 'inherited'
        self.assertEqual(visibleAttr.Get(), 'inherited')

        # visibility token must not exists now in the USD data model after undo
        self.assertFalse(bool(primSpec and UsdGeom.Tokens.visibility in primSpec.attributes))

        # capsuleItem must be visible now
        self.assertTrue(object3d.visibility())

        # redo
        visibleCmd.redo()

        # expect the visibility attribute to be 'invisible'
        self.assertEqual(visibleAttr.Get(), 'invisible')

        # visibility token must exists now in the USD data model after redo
        self.assertTrue(bool(primSpec and UsdGeom.Tokens.visibility in primSpec.attributes))

        # capsuleItem must be invisible now
        self.assertFalse(object3d.visibility())

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testMayaHideAndShowHiddenUndoCommands only available in UFE v2 or greater.')
    def testMayaHideAndShowHiddenUndoCommands(self):
        ''' Verify the token / attribute values for visibility via "hide", "showHidden" commands + Undo/Redo '''

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

        # create a Capsule and Cylinder via contextOps menu
        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'])
        proxyShapeContextOps.doOp(['Add New Prim', 'Cylinder'])

        # capsule
        capsulePath = ufe.PathString.path('%s,/Capsule1' % proxyShape)
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        capsulePrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(capsulePath))

        # cylinder
        cylinderPath = ufe.PathString.path('%s,/Cylinder1' % proxyShape)
        cylinderItem = ufe.Hierarchy.createItem(cylinderPath)
        cylinderPrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(cylinderPath))

        # stage / primSpec
        stage = mayaUsd.ufe.getStage(str(proxyShapePath))
        primSpecCapsule = stage.GetEditTarget().GetPrimSpecForScenePath('/Capsule1');
        primSpecCylinder = stage.GetEditTarget().GetPrimSpecForScenePath('/Cylinder1');

        # select capsule and cylinder prims
        ufe.GlobalSelection.get().append(capsuleItem)
        ufe.GlobalSelection.get().append(cylinderItem)

        # hide selected items
        cmds.hide(cs=True)

        # get the visibility "attribute"
        capsuleVisibleAttr = capsulePrim.GetAttribute('visibility')
        cylinderVisibleAttr = cylinderPrim.GetAttribute('visibility')

        # expect the visibility attribute to be 'invisible'
        self.assertEqual(capsuleVisibleAttr.Get(), 'invisible')
        self.assertEqual(cylinderVisibleAttr.Get(), 'invisible')

        # visibility "token" must exists now in the USD data model
        self.assertTrue(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertTrue(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

        # undo
        cmds.undo()

        # expect the visibility attribute to be 'inherited'
        self.assertEqual(capsuleVisibleAttr.Get(), 'inherited')
        self.assertEqual(cylinderVisibleAttr.Get(), 'inherited')

        # visibility token must not exists now in the USD data model after undo
        self.assertFalse(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertFalse(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

        # undo
        cmds.redo()

        # expect the visibility attribute to be 'invisible'
        self.assertEqual(capsuleVisibleAttr.Get(), 'invisible')
        self.assertEqual(cylinderVisibleAttr.Get(), 'invisible')

        # visibility "token" must exists now in the USD data model
        self.assertTrue(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertTrue(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

        # hide selected items again
        cmds.hide(cs=True)

        # right after, call showHidden -all to make everything visible again
        cmds.showHidden( all=True )

        # expect the visibility attribute to be 'inherited'
        self.assertEqual(capsuleVisibleAttr.Get(), 'inherited')
        self.assertEqual(cylinderVisibleAttr.Get(), 'inherited')

        # This time, expect the visibility token to exists in the USD data model
        self.assertTrue(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertTrue(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

        # undo the showHidden command
        cmds.undo()

        # expect the visibility attribute to be 'invisible'
        self.assertEqual(capsuleVisibleAttr.Get(), 'invisible')
        self.assertEqual(cylinderVisibleAttr.Get(), 'invisible')

        # visibility "token" must exists now in the USD data model
        self.assertTrue(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertTrue(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

        # redo 
        cmds.redo()

        # expect the visibility attribute to be 'inherited'
        self.assertEqual(capsuleVisibleAttr.Get(), 'inherited')
        self.assertEqual(cylinderVisibleAttr.Get(), 'inherited')

        # visibility "token" must exists now in the USD data model
        self.assertTrue(bool(primSpecCapsule and UsdGeom.Tokens.visibility in primSpecCapsule.attributes))
        self.assertTrue(bool(primSpecCylinder and UsdGeom.Tokens.visibility in primSpecCylinder.attributes))

    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
        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'])

        # capsule
        capsulePath = ufe.PathString.path('%s,/Capsule1' % proxyShape)
        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))

    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
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.PathString.path(proxyShape)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)
        proxyShapeContextOps.doOp(['Add New Prim', 'Xform'])
        xformPath = ufe.PathString.path('%s,/Xform1' % proxyShape)
        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(proxyShape)
        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('%s,/Xform1/Sphere1' % proxyShape)
        sphere1Item = ufe.Hierarchy.createItem(sphere1Path)
        sphere1Prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(sphere1Path))
        UsdGeom.XformCommonAPI(sphere1Prim).SetTranslate((-5, 0, 0))

        sphere2Path = ufe.PathString.path('%s,/Xform1/Sphere2' % proxyShape)
        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))
コード例 #13
0
class AttributeBlockTestCase(unittest.TestCase):

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)

        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

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

        # Clear selection to start off
        cmds.select(clear=True)

    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)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # cylinder prim
        cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
        self.assertIsNotNone(cylinderPrim)

        # author a new radius value
        self.assertTrue(cylinderPrim.HasAttribute('doubleSided'))
        doubleSidedAttr = cylinderPrim.GetAttribute('doubleSided')

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

        # 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)

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

    def testTransformationAttributeBlocking(self):
        '''Authoring transformation 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)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # cylinder prim
        cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
        self.assertIsNotNone(cylinderPrim)

        # create 3 sub-layers ( LayerA, LayerB, LayerC ) and set the edit target to LayerB.
        rootLayer = stage.GetRootLayer()
        subLayerC = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerC")[0]
        subLayerB = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerB")[0]
        subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerA")[0]

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

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

        # create a transform3d
        cylinderPath = ufe.Path([
            mayaUtils.createUfePathSegment("|mayaUsdTransform|shape"),
            usdUtils.createUfePathSegment("/pCylinder1")
        ])
        cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

        cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)

        # create a xformable
        cylinderXformable = UsdGeom.Xformable(cylinderPrim)

        # writing to "transform op order" is expected to be allowed.
        self.assertTrue(
            mayaUsdUfe.isAttributeEditAllowed(
                cylinderXformable.GetXformOpOrderAttr()))

        # do any transform editing.
        cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)
        cylinderT3d.scale(2.5, 2.5, 2.5)
        cylinderT3d.translate(4.0, 2.0, 3.0)

        # check the "transform op order" stack.
        self.assertEqual(cylinderXformable.GetXformOpOrderAttr().Get(),
                         Vt.TokenArray(('xformOp:translate', 'xformOp:scale')))

        # check if translate attribute is editable
        translateAttr = cylinderPrim.GetAttribute('xformOp:translate')
        self.assertIsNotNone(translateAttr)

        # authoring new transformation edit is expected to be allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(translateAttr))
        cylinderT3d.translate(5.0, 6.0, 7.0)
        self.assertEqual(translateAttr.Get(), Gf.Vec3d(5.0, 6.0, 7.0))

        # set the edit target to a weaker layer (LayerC)
        cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerC)

        # authoring new transformation edit is not allowed.
        self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(translateAttr))

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

        # authoring new transformation edit is allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(translateAttr))
        cylinderT3d.rotate(0.0, 90.0, 0.0)

        # check the "transform op order" stack.
        self.assertEqual(
            cylinderXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(
                ('xformOp:translate', 'xformOp:rotateXYZ', 'xformOp:scale')))

    def testAttributeBlocking3dCommonApi(self):
        '''
        Verify authoring transformation attribute(s) in weaker layer(s) are not permitted if there exist opinion(s) 
        in stronger layer(s) when using UsdGeomXformCommonAPI.
        '''

        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # cylinder prim
        cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
        self.assertIsNotNone(cylinderPrim)

        # create 3 sub-layers ( LayerA, LayerB, LayerC ) and set the edit target to LayerB.
        rootLayer = stage.GetRootLayer()
        subLayerC = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerC")[0]
        subLayerB = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerB")[0]
        subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerA")[0]

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

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

        # create a xformable and give it a common API single "pivot".
        # This will match the common API, but not the Maya API.
        cylinderXformable = UsdGeom.Xformable(cylinderPrim)

        cylinderXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat,
                                         "pivot")
        cylinderXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat,
                                         "pivot", True)

        self.assertEqual(
            cylinderXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(("xformOp:translate:pivot",
                           "!invert!xformOp:translate:pivot")))
        self.assertTrue(UsdGeom.XformCommonAPI(cylinderXformable))

        # Now that we have set up the transform stack to be common API-compatible,
        # create the Transform3d interface.
        cylinderPath = ufe.Path([
            mayaUtils.createUfePathSegment("|mayaUsdTransform|shape"),
            usdUtils.createUfePathSegment("/pCylinder1")
        ])
        cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

        cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)

        # select the cylinderItem
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(cylinderItem)

        # rotate the cylinder
        cmds.rotate(0,
                    90,
                    0,
                    relative=True,
                    objectSpace=True,
                    forceOrderXYZ=True)

        self.assertEqual(
            cylinderXformable.GetXformOpOrderAttr().Get(),
            Vt.TokenArray(("xformOp:translate:pivot", "xformOp:rotateXYZ",
                           "!invert!xformOp:translate:pivot")))
        self.assertTrue(UsdGeom.XformCommonAPI(cylinderXformable))

        # set the edit target to a weaker layer (LayerC)
        cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerC)

        # check if rotate attribute is editable
        rotateAttr = cylinderPrim.GetAttribute('xformOp:rotateXYZ')
        self.assertIsNotNone(rotateAttr)

        # authoring new transformation edit is not allowed.
        self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(rotateAttr))

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

        # authoring new transformation edit is allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(rotateAttr))

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testAttributeBlocking3dMatrixOps only available in UFE v2 or greater.'
    )
    def testAttributeBlocking3dMatrixOps(self):
        '''
        Verify authoring transformation attribute(s) in weaker layer(s) are not permitted if there exist opinion(s) 
        in stronger layer(s) when using UsdTransform3dMatrixOp.
        '''

        # create new stage
        cmds.file(new=True, force=True)

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

        # get the stage
        proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
        proxyShapePath = proxyShapes[0]
        stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage()

        # cylinder prim
        cylinderPrim = stage.GetPrimAtPath('/pCylinder1')
        self.assertIsNotNone(cylinderPrim)

        # create 3 sub-layers ( LayerA, LayerB, LayerC ) and set the edit target to LayerB.
        rootLayer = stage.GetRootLayer()
        subLayerC = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerC")[0]
        subLayerB = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerB")[0]
        subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier,
                                            edit=True,
                                            addAnonymous="SubLayerA")[0]

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

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

        # create a xformable and give it a matrix4d xformOp:transform
        # This will match the UsdTransform3dMatrixOp, but not the Maya API.
        cylinderXformable = UsdGeom.Xformable(cylinderPrim)
        transformOp = cylinderXformable.AddTransformOp()
        xform = Gf.Matrix4d(1.0).SetTranslate(Gf.Vec3d(10, 20, 30))
        transformOp.Set(xform)
        self.assertTrue(
            cylinderPrim.GetPrim().HasAttribute("xformOp:transform"))

        # Now that we have set up the transform stack to be 3dMatrixOp compatible,
        # create the Transform3d interface.
        cylinderPath = ufe.Path([
            mayaUtils.createUfePathSegment("|mayaUsdTransform|shape"),
            usdUtils.createUfePathSegment("/pCylinder1")
        ])
        cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

        cylinderT3d = ufe.Transform3d.transform3d(cylinderItem)

        # select the cylinderItem
        sn = ufe.GlobalSelection.get()
        sn.clear()
        sn.append(cylinderItem)

        # move
        cmds.move(65, 20, 10)
        self.assertEqual(cylinderXformable.GetXformOpOrderAttr().Get(),
                         Vt.TokenArray(('xformOp:transform', )))

        # validate the Matrix4d entires
        actual = cylinderXformable.GetLocalTransformation()
        expected = Gf.Matrix4d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 65, 20, 10,
                               1)

        self.assertTrue(Gf.IsClose(actual, expected, 1e-5))

        # set the edit target to a weaker layer (LayerC)
        cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerC)

        # check if transform attribute is editable
        transformAttr = cylinderPrim.GetAttribute('xformOp:transform')
        self.assertIsNotNone(transformAttr)

        # authoring new transformation edit is not allowed.
        self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(transformAttr))

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

        # authoring new transformation edit is allowed.
        self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(transformAttr))
コード例 #14
0
class SelectTestCase(unittest.TestCase):
    '''Verify UFE selection 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
        self.assertTrue(self.pluginsLoaded)

        # Load a file that has the same scene in both the Maya Dag
        # hierarchy and the USD hierarchy.
        mayaUtils.openTestScene("parentCmd", "simpleSceneMayaPlusUSD_TRS.ma")

        # Create multiple scene items.  We will alternate between selecting a
        # Maya item and a USD item, 3 items each data model, one item at a
        # time, so we select 6 different items, one at a time.
        shapeSegment = mayaUtils.createUfePathSegment(
            "|mayaUsdProxy1|mayaUsdProxyShape1")
        ufeNames = ["cubeXform", "cylinderXform", "sphereXform"]
        mayaNames = ["pCube1", "pCylinder1", "pSphere1"]
        usdPaths = []
        for n in ["/" + o for o in ufeNames]:
            usdPaths.append(
                ufe.Path([shapeSegment,
                          usdUtils.createUfePathSegment(n)]))
        mayaPaths = []
        for n in ["|" + o for o in mayaNames]:
            mayaPaths.append(ufe.Path(mayaUtils.createUfePathSegment(n)))

        # Create a list of paths by alternating USD objects and Maya objects
        # Flatten zipped tuples using list comprehension double loop.
        zipped = zip(usdPaths, mayaPaths)
        paths = [j for i in zipped for j in i]

        # Create items for all paths.
        self.items = [ufe.Hierarchy.createItem(p) for p in paths]

        # Clear selection to start off
        cmds.select(clear=True)

    def runTestSelection(self, selectCmd):
        '''Run the replace selection test, using the argument command to
        replace the selection with a single scene item.'''

        # Clear the selection.
        globalSn = ufe.GlobalSelection.get()
        globalSn.clear()
        self.assertTrue(globalSn.empty())

        # Select all items in turn.
        for item in self.items:
            selectCmd(item)

        # Item in the selection should be the last item in our list.
        self.assertEqual(len(globalSn), 1)

        def snFront(sn):
            return next(iter(sn)) if \
                ufe.VersionInfo.getMajorVersion() == 1 else sn.front()

        self.assertEqual(snFront(globalSn), self.items[-1])

        # Check undo.  For this purpose, re-create the list of items in reverse
        # order.  Because we're already at the last item, we skip the last one
        # (i.e. last item is -1, so start at -2, and increment by -1).
        rItems = self.items[-2::-1]

        # Undo until the first element, checking the selection as we go.
        for i in rItems:
            cmds.undo()
            self.assertEqual(len(globalSn), 1)
            self.assertEqual(snFront(globalSn), i)

        # Check redo.
        fItems = self.items[1:]

        # Redo until the last element, checking the selection as we go.
        for i in fItems:
            cmds.redo()
            self.assertEqual(len(globalSn), 1)
            self.assertEqual(snFront(globalSn), i)

    def testUfeSelect(self):
        def selectCmd(item):
            sn = ufe.Selection()
            sn.append(item)
            ufeSelectCmd.replaceWith(sn)

        self.runTestSelection(selectCmd)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2,
                         'testMayaSelect only available in UFE v2 or greater.')
    def testMayaSelect(self):
        # Maya PR 121 now has support for UFE path string in select command.
        def selectCmd(item):
            cmds.select(ufe.PathString.string(item.path()))

        self.runTestSelection(selectCmd)

    @unittest.skipUnless(
        ufeUtils.ufeFeatureSetVersion() >= 2,
        'testMayaSelectFlags only available in UFE v2 or greater.')
    def testMayaSelectFlags(self):
        # Maya PR 121 now has support for UFE path string in select command.

        # Clear the selection.
        globalSn = ufe.GlobalSelection.get()
        globalSn.clear()
        self.assertTrue(globalSn.empty())

        # Incrementally add to the selection all items in turn.
        # Also testing undo/redo along the way.
        cnt = 0
        for item in self.items:
            cnt += 1
            cmds.select(ufe.PathString.string(item.path()), add=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt - 1, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

        # Since we added all the items to the global selection, it
        # should have all of them.
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        # Incrementally remove from the selection all items in turn.
        # Also testing undo/redo along the way.
        for item in self.items:
            cnt -= 1
            cmds.select(ufe.PathString.string(item.path()), deselect=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt + 1, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

        # Since we removed all items from global selection it
        # should be empty now.
        self.assertTrue(globalSn.empty())

        # Incrementally toggle selection state of all items in turn.
        # Since they all start unselected, they will toggle to selected.
        # Also testing undo/redo along the way.
        globalSn.clear()
        self.assertTrue(globalSn.empty())
        cnt = 0
        for item in self.items:
            cnt += 1
            cmds.select(ufe.PathString.string(item.path()), toggle=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt - 1, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

        # Since we toggled each item to selected, we should have all
        # of them on the global selection.
        self.assertEqual(len(globalSn), len(self.items))

        # Incrementally toggle selection state of all items in turn.
        # Since they all start selected, they will toggle to unselected.
        # Also testing undo/redo along the way.
        for item in self.items:
            cnt -= 1
            cmds.select(ufe.PathString.string(item.path()), toggle=True)
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

            cmds.undo()
            self.assertEqual(cnt + 1, len(globalSn))
            self.assertTrue(globalSn.contains(item.path()))

            cmds.redo()
            self.assertEqual(cnt, len(globalSn))
            self.assertFalse(globalSn.contains(item.path()))

        # Since we toggled all items to unselected, the global selection
        # should be empty now.
        self.assertTrue(globalSn.empty())

        # Select all the items at once, replacing the existing selection.
        # Also testing undo/redo.
        pathStrings = [ufe.PathString.string(i.path()) for i in self.items]
        cmds.select(*pathStrings, replace=True)
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        cmds.undo()
        self.assertEqual(0, len(globalSn))
        self.assertTrue(globalSn.empty())

        cmds.redo()
        self.assertEqual(len(globalSn), len(self.items))

        # Ensure the global selection order is the same as our item order.
        itemIt = iter(self.items)
        for selIt in globalSn:
            self.assertEqual(selIt, next(itemIt))

        # With all items selected (and in same order as item order)
        # "select -add" the first item which will move it to the end.
        first = self.items[0]
        self.assertEqual(globalSn.front(), first)
        cmds.select(ufe.PathString.string(first.path()), add=True)
        self.assertTrue(globalSn.contains(first.path()))
        self.assertEqual(globalSn.back(), first)

    @unittest.skipUnless((
        (ufeUtils.ufeFeatureSetVersion() >= 2) and
        (mayaUtils.previewReleaseVersion() >= 123)
    ), 'testMayaSelectMuteLayer only available in UFE v2 or greater and Maya Preview Release 123 or later.'
                         )
    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())

    @unittest.skipUnless((
        (ufeUtils.ufeFeatureSetVersion() >= 2) and
        (mayaUtils.previewReleaseVersion() >= 123)
    ), 'testMayaSelectSwitchVariant only available in UFE v2 or greater and Maya Preview Release 123 or later.'
                         )
    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())

    @unittest.skipUnless(
        mayaUtils.previewReleaseVersion() >= 126,
        'Requires Maya fixes only available in Maya Preview Release 126 or later.'
    )
    def testMayaSelectUndoPrimCreation(self):
        '''Test if the SceneItem's prim is still valid on selection after the prim creation is undone then redone'''

        # helper function to check if the current
        # selected SceneItem's prim is valid.
        def checkSelectedSceneItemPrim(expectedSceneItem):
            globalSn = ufe.GlobalSelection.get()
            self.assertEqual(len(globalSn), 1)
            sceneItem = globalSn.front()
            self.assertEqual(sceneItem, expectedSceneItem)
            prim = mayaUsd.ufe.getPrimFromRawItem(sceneItem.getRawAddress())
            self.assertTrue(prim)

        shapeNode, shapeStage = mayaUtils.createProxyAndStage()
        proxyShapePath = ufe.PathString.path(shapeNode)
        proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath)
        proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem)

        with mayaUsd.lib.UsdUndoBlock():
            proxyShapeContextOps.doOp(['Add New Prim', 'Capsule'])

        with mayaUsd.lib.UsdUndoBlock():
            proxyShapeContextOps.doOp(['Add New Prim', 'Cube'])

        capsulePath = ufe.PathString.path('%s,/Capsule1' % shapeNode)
        capsuleItem = ufe.Hierarchy.createItem(capsulePath)
        cmds.select(ufe.PathString.string(capsuleItem.path()), replace=True)
        checkSelectedSceneItemPrim(capsuleItem)

        cubePath = ufe.PathString.path('%s,/Cube1' % shapeNode)
        cubeItem = ufe.Hierarchy.createItem(cubePath)
        cmds.select(ufe.PathString.string(cubeItem.path()), replace=True)
        checkSelectedSceneItemPrim(cubeItem)

        cmds.undo()  # undo the selection of the cube
        cmds.undo()  # undo the selection of the capsule
        cmds.undo()  # undo the creation of the cube
        cmds.undo()  # undo the creation of the capsule

        cmds.redo()  # redo the creation of the capsule
        cmds.redo()  # redo the creation of the cube
        cmds.redo()  # redo the selection of the capsule
        checkSelectedSceneItemPrim(capsuleItem)
        cmds.redo()  # redo the selection of the cube
        checkSelectedSceneItemPrim(cubeItem)
        cmds.undo()  # undo the selection of the cube
        checkSelectedSceneItemPrim(capsuleItem)
コード例 #15
0
class testLayerManagerSerialization(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls._inputPath = fixturesUtils.setUpClass(__file__)

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

    def setupEmptyScene(self):
        self._currentTestDir = tempfile.mkdtemp(prefix='LayerManagerTest')
        self._tempMayaFile = os.path.join(
            self._currentTestDir, 'SerializationTest.ma')
        cmds.file(rename=self._tempMayaFile)

    def copyTestFilesAndMakeEdits(self):
        self._currentTestDir = tempfile.mkdtemp(prefix='LayerManagerTest')
        fromDirectory = os.path.join(
            self._inputPath, 'LayerManagerSerializationTest')
        copy_tree(fromDirectory, self._currentTestDir)

        self._tempMayaFile = os.path.join(
            self._currentTestDir, 'SerializationTest.ma')
        self._rootUsdFile = os.path.join(
            self._currentTestDir, 'SerializationTest.usda')
        self._test1File = os.path.join(
            self._currentTestDir, 'SerializationTest_1.usda')
        self._test1_1File = os.path.join(
            self._currentTestDir, 'SerializationTest_1_1.usda')
        self._test2File = os.path.join(
            self._currentTestDir, 'SerializationTest_2.usda')
        self._test2_1File = os.path.join(
            self._currentTestDir, 'SerializationTest_2_1.usda')

        cmds.file(self._tempMayaFile, open=True, force=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        stage.SetEditTarget(stage.GetRootLayer())
        newPrimPath = "/ChangeInRoot"
        stage.DefinePrim(newPrimPath, "xform")

        stage.SetEditTarget(stack[2])
        newPrimPath = "/ChangeInLayer_1_1"
        stage.DefinePrim(newPrimPath, "xform")

        stage.SetEditTarget(stack[0])
        newPrimPath = "/ChangeInSessionLayer"
        stage.DefinePrim(newPrimPath, "xform")

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        cleanCount = 0
        dirtyCount = 0
        for l in stack:
            if l.dirty:
                dirtyCount += 1
            else:
                cleanCount += 1
        self.assertEqual(3, dirtyCount)
        self.assertEqual(3, cleanCount)

        return stage

    def confirmEditsSavedStatus(self, fileBackedSavedStatus, sessionSavedStatus):
        cmds.file(new=True, force=True)

        proxyNode, stage = createProxyFromFile(self._rootUsdFile)

        newPrimPath = "/ChangeInRoot"
        self.assertEqual(
            fileBackedSavedStatus, stage.GetPrimAtPath(newPrimPath).IsValid())

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertEqual(
            fileBackedSavedStatus, stage.GetPrimAtPath(newPrimPath).IsValid())

        newPrimPath = "/ChangeInSessionLayer"
        self.assertEqual(
            sessionSavedStatus, stage.GetPrimAtPath(newPrimPath).IsValid())

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testSaveAllToMaya is available only in UFE v2 or greater.')
    def testSaveAllToMaya(self):
        '''
        Verify that all USD edits are save into the Maya file.
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(False, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testSaveAllToUsd is available only in UFE v2 or greater.')
    def testSaveAllToUsd(self):
        '''
        Verify that all USD edits are saved back to the original .usd files
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertTrue(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(True, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testIgnoreAllUsd is available only in UFE v2 or greater.')
    def testIgnoreAllUsd(self):
        '''
        Verify that all USD edits are ignored
        '''
        stage = self.copyTestFilesAndMakeEdits()

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

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

        cmds.file(self._tempMayaFile, open=True)

        stage = mayaUsd.ufe.getStage(
            "|SerializationTest|SerializationTestShape")
        stack = stage.GetLayerStack()
        self.assertEqual(6, len(stack))

        newPrimPath = "/ChangeInRoot"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInLayer_1_1"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        newPrimPath = "/ChangeInSessionLayer"
        self.assertFalse(stage.GetPrimAtPath(newPrimPath))

        self.confirmEditsSavedStatus(False, False)

        shutil.rmtree(self._currentTestDir)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testAnonymousRootToMaya is available only in UFE v2 or greater.')
    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)

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testAnonymousRootToMaya is available only in UFE v2 or greater.')
    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)

    def testMultipleFormatsSerialisation(self):
        # Test setup
        self.setupEmptyScene()
        extensions = ["usd", "usda", "usdc"]
        usdFilePath = os.path.abspath('MultipleFormatsSerializationTest.usda')

        def createProxyShapeFromFile(filePath):
            usdNode = cmds.createNode('mayaUsdProxyShape', skipSelect=True, name='UsdStageShape')
            cmds.setAttr(usdNode + '.filePath', filePath, type='string')
            return usdNode

        def getStageFromProxyShape(shapeNode):
            return mayaUsdLib.GetPrim(shapeNode).GetStage()

        # Initialise the sublayers
        cmds.file(new=True, force=True)
        cmds.file(rename=self._tempMayaFile)
        rootLayer = Sdf.Layer.CreateNew(usdFilePath)
        layers = []
        for ext in extensions:
            filePath = os.path.abspath('TestLayer.{}'.format(ext))
            layer = Sdf.Layer.CreateNew(filePath)
            layer.comment = ext
            layer.Save()
            layers.append(layer)
            rootLayer.subLayerPaths.append(filePath)
        rootLayer.Save()

        # Add edits to the 3 format layers and save the maya scene
        proxyShape = createProxyShapeFromFile(usdFilePath)
        stage = getStageFromProxyShape(proxyShape)
        for subLayerPath in stage.GetRootLayer().subLayerPaths:
            layer = Sdf.Layer.Find(subLayerPath)
            with Usd.EditContext(stage, layer):
                stage.DefinePrim("/{}".format(layer.GetFileFormat().primaryFileExtension))
        self.assertTrue(stage.GetSessionLayer().empty)
        cmds.file(save=True, force=True)

        # Assert the edits have been restored
        cmds.file(new=True, force=True)
        cmds.file(self._tempMayaFile, open=True)
        stage = getStageFromProxyShape(proxyShape)
        stageLayers = stage.GetUsedLayers()

        # root + session + 3 format layers
        self.assertEqual(len(stageLayers), 5)
        
        # Check they all exist
        for layer in layers:
            self.assertIn(layer, stageLayers)
        
        # Check their content exists
        for ext in extensions:
            prim = stage.GetPrimAtPath("/{}".format(ext))
            self.assertTrue(prim.IsValid())

        # Cleanup
        cmds.file(new=True, force=True)
        shutil.rmtree(self._currentTestDir)
コード例 #16
0
class Transform3dMatricesTestCase(unittest.TestCase):
    '''Verify the Transform3d matrix interfaces.'''

    pluginsLoaded = False

    @classmethod
    def setUpClass(cls):
        fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)

        if not cls.pluginsLoaded:
            cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()

    @classmethod
    def tearDownClass(cls):
        standalone.uninitialize()

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

        # Open usdCylinder.ma scene in testSamples
        mayaUtils.openCylinderScene()

    def assertMatrixAlmostEqual(self, ma, mb):
        for ra, rb in zip(ma, mb):
            for a, b in zip(ra, rb):
                self.assertAlmostEqual(a, b)

    def testMatrices(self):
        '''Test inclusive and exclusive matrices.'''

        # Scene hierarchy:
        #
        #   mayaUsdTransform 
        #   |_ shape
        #      |_ pCylinder1 (USD mesh)
        #
        # transform has a translation of 10 units along the Y axis,
        # and a rotation of 30 degrees around X.  pCylinder1 has a further
        # 30 degree rotation around X (60 degrees total).

        # Create a non-identity transform for the Maya segment of the UFE path:
        # translate the transform of the proxy shape up (Y axis) by 10
        # units, rotate around X 30 degrees.
        cmds.setAttr('mayaUsdTransform.translateY', 10)
        cmds.setAttr('mayaUsdTransform.rotateX', 30)

        # Next, rotate the USD object by another 30 degrees around X.  To
        # do so, select it first.
        mayaPathSegment = mayaUtils.createUfePathSegment('|mayaUsdTransform|shape')
        usdPathSegment = usdUtils.createUfePathSegment('/pCylinder1')
        cylinderPath = ufe.Path([mayaPathSegment, usdPathSegment])
        cylinderItem = ufe.Hierarchy.createItem(cylinderPath)

        ufe.GlobalSelection.get().append(cylinderItem)

        cmds.rotate(30, r=True, os=True, rotateX=True)

        # Get a Transform3d interface to the cylinder.
        t3d = ufe.Transform3d.transform3d(cylinderItem)

        # Get the inclusive and exclusive matrices for the cylinder, and
        # for its (last) segment.
        cylSegExclMat = t3d.segmentExclusiveMatrix()
        cylSegInclMat = t3d.segmentInclusiveMatrix()
        cylExclMat = t3d.exclusiveMatrix()
        cylInclMat = t3d.inclusiveMatrix()

        # Since the cylinder has no USD parent, its segment exclusive
        # matrix is the identity, and its segment inclusive matrix is just
        # its 30 degree rotation around X.
        self.assertMatrixAlmostEqual(cylSegExclMat.matrix, identityMatrix)
        self.assertMatrixAlmostEqual(cylSegInclMat.matrix, rotXMatrix(radians(30)))

        # Get a Transform3d interface for the proxy shape's transform, and
        # get its transforms.
        xformPath = ufe.Path(mayaUtils.createUfePathSegment('|mayaUsdTransform'))
        xformItem = ufe.Hierarchy.createItem(xformPath)
        t3d = ufe.Transform3d.transform3d(xformItem)

        xformSegExclMat = t3d.segmentExclusiveMatrix()
        xformSegInclMat = t3d.segmentInclusiveMatrix()
        xformExclMat = t3d.exclusiveMatrix()
        xformInclMat = t3d.inclusiveMatrix()

        # Since the transform has no Maya parent, its segment exclusive
        # matrix is the identity, and its segment inclusive matrix is 
        # its 30 degree rotation around X, and its 10 unit Y translation.
        sx30 = sin(radians(30))
        cx30 = cos(radians(30))
        self.assertMatrixAlmostEqual(xformSegExclMat.matrix, identityMatrix)
        self.assertMatrixAlmostEqual(
            xformSegInclMat.matrix, [[1, 0, 0, 0], [0, cx30, sx30, 0],
                                     [0, -sx30, cx30, 0], [0, 10, 0, 1]])

        # Since the transform is the cylinder's parent, the cylinder's
        # exclusive matrix must be (by definition) the transform's
        # inclusive matrix.
        self.assertMatrixAlmostEqual(xformInclMat.matrix, cylExclMat.matrix)

        # The cylinder's inclusive matrix is the full 60 degree rotation
        # around X, and 10 unit Y translation.
        sx60 = sin(radians(60))
        cx60 = cos(radians(60))
        self.assertMatrixAlmostEqual(
            cylInclMat.matrix, [[1, 0, 0, 0], [0, cx60, sx60, 0],
                                [0, -sx60, cx60, 0], [0, 10, 0, 1]])

    @unittest.skipUnless(ufeUtils.ufeFeatureSetVersion() >= 2, 'testTransform3dMatrixOpAccessors only available in UFE v2 or greater.')
    def testTransform3dMatrixOpAccessors(self):
        '''Matrix transform op TRS must match separate transform op TRS.'''

        testFile = testUtils.getTestScene("transform3d", "TranslateRotate_vs_xform.usda")
        (psPath, stage) = mayaUtils.createProxyFromFile(testFile)

        # cam1 has the "transform" transform op (i.e. a matrix), and cam2 has
        # translate + rotateXYZ transform ops.
        cam1 = ufe.Hierarchy.createItem(
            ufe.PathString.path(psPath + ",/cameras/cam1"))
        cam2 = ufe.Hierarchy.createItem(
            ufe.PathString.path(psPath + ",/cameras/cam2"))
        cam1t3d = ufe.Transform3d.transform3d(cam1)
        cam2t3d = ufe.Transform3d.transform3d(cam2)
        cam1T = cam1t3d.translation()
        cam2T = cam2t3d.translation()
        testUtils.assertVectorAlmostEqual(self, cam1T.vector, cam2T.vector)
        cam1R = cam1t3d.rotation()
        cam2R = cam2t3d.rotation()
        testUtils.assertVectorAlmostEqual(self, cam1R.vector, cam2R.vector)
コード例 #17
0
    def testWrappers(self):
        ''' Verify the python wappers.'''

        # Create empty stage and add a prim.
        import mayaUsd_createStageWithNewLayer
        proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
        proxyShapePath = ufe.Path(mayaUtils.createUfePathSegment(proxyShape))

        # Test maya-usd getStage() wrapper.
        mayaUsdStage = mayaUsd.ufe.getStage(str(proxyShapePath))
        self.assertIsNotNone(mayaUsdStage)

        # Verify that this the stage returned from maya-usd wrapper
        # is same as one from Pixar wrapper.
        shapeNode = cmds.ls(sl=True, l=True)[0]
        pixarStage = mayaUsd.lib.GetPrim(shapeNode).GetStage()
        self.assertIsNotNone(pixarStage)
        self.assertIs(mayaUsdStage, pixarStage)

        # Test maya-usd stagePath() wrapper.
        stagePath = mayaUsd.ufe.stagePath(mayaUsdStage)
        self.assertIsNotNone(stagePath)

        # It should also be the same as the shape node string
        # (minus the extra |world at front).
        self.assertEqual(shapeNode, stagePath.replace('|world', ''))

        # Test the maya-usd ufePathToPrim() wrapper.
        mayaUsdStage.DefinePrim("/Capsule1", "Capsule")
        if ufeUtils.ufeFeatureSetVersion() >= 2:
            capsulePrim = mayaUsd.ufe.ufePathToPrim('%s,/Capsule1' %
                                                    proxyShape)
        else:
            capsulePrim = mayaUsd.ufe.ufePathToPrim('|world%s,/Capsule1' %
                                                    proxyShape)
        self.assertIsNotNone(capsulePrim)

        if ufeUtils.ufeFeatureSetVersion() >= 2:
            # Test the maya-usd getPrimFromRawItem() wrapper.
            capsulePath = proxyShapePath + usdUtils.createUfePathSegment(
                '/Capsule1')
            capsuleItem = ufe.Hierarchy.createItem(capsulePath)
            rawItem = capsuleItem.getRawAddress()
            capsulePrim2 = mayaUsd.ufe.getPrimFromRawItem(rawItem)
            self.assertIsNotNone(capsulePrim2)
            self.assertEqual(capsulePrim, capsulePrim2)

            # Test the maya-usd getNodeTypeFromRawItem() wrapper.
            nodeType = mayaUsd.ufe.getNodeTypeFromRawItem(rawItem)
            self.assertIsNotNone(nodeType)

            # Test the maya-usd getNodeNameFromRawItem() wrapper.
            nodeName = mayaUsd.ufe.getNodeNameFromRawItem(rawItem)
            self.assertIsNotNone(nodeName)

        # Test the maya-usd runtime id wrappers.
        mayaRtid = mayaUsd.ufe.getMayaRunTimeId()
        usdRtid = mayaUsd.ufe.getUsdRunTimeId()
        self.assertTrue(mayaRtid > 0)
        self.assertTrue(usdRtid > 0)
        self.assertNotEqual(mayaRtid, usdRtid)