def testRenameRestrictionHasSpecs(self): '''Restrict renaming USD node. Cannot rename a node that doesn't contribute to the final composed prim''' # open appleBite.ma scene in testSamples mayaUtils.openAppleBiteScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|Asset_flattened_instancing_and_class_removed_usd|Asset_flattened_instancing_and_class_removed_usdShape' ) usdPathSegment = usdUtils.createUfePathSegment('/apple/payload/geo') geoPath = ufe.Path([mayaPathSegment, usdPathSegment]) geoItem = ufe.Hierarchy.createItem(geoPath) ufe.GlobalSelection.get().append(geoItem) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # rename "/apple/payload/geo" to "/apple/payload/geo_renamed" # expect the exception happens with self.assertRaises(RuntimeError): cmds.rename("geo_renamed")
def checkParentDone(): # The xform now has the capsule as its child. xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertIn('Capsule1', childrenNames(xformChildren)) # Confirm that the capsule has not moved in world space. Must # re-create the prim and its scene item, as its path has changed. capsulePath = ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/Xform1/Capsule1') ]) capsulePrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(capsulePath)) capsuleXformable = UsdGeom.Xformable(capsulePrim) capsuleItem = ufe.Hierarchy.createItem(capsulePath) capsuleT3d = ufe.Transform3d.transform3d(capsuleItem) capsuleWorld = capsuleT3d.inclusiveMatrix() assertVectorAlmostEqual(self, capsuleWorldPre, matrixToList(capsuleWorld)) # The capsule's transform ops now have Maya fallback stack ops. A # scale fallback op is added, even though it's uniform unit. self.assertEqual( capsuleXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray( ("xformOp:translate", "xformOp:rotateXYZ", "xformOp:rotateX", "xformOp:translate:maya_fallback", "xformOp:rotateXYZ:maya_fallback", "xformOp:scale:maya_fallback")))
def testMoveUSD(self): '''Move USD object, read through the Transform3d interface.''' # Select Ball_35 to move 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 translation with the USD run-time translation. To # obtain the full translation of Ball_35, we need to add the USD # translation to the Maya proxy shape translation. 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()) # Set up the callables that will retrieve the translation. self.runTimeTranslation = ball35Translation self.ufeTranslation = partial(transform3dTranslation, transform3d) # Save the initial position to the memento list. expected = ball35Translation() self.runTestMove(expected)
def checkParentDone(): # The xform now has the capsule as its child. xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertIn('Capsule1', childrenNames(xformChildren)) # Confirm that the capsule has not moved in world space. Must # re-create the prim and its scene item, as its path has changed. capsulePath = ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/Xform1/Capsule1') ]) capsulePrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(capsulePath)) capsuleXformable = UsdGeom.Xformable(capsulePrim) capsuleItem = ufe.Hierarchy.createItem(capsulePath) capsuleT3d = ufe.Transform3d.transform3d(capsuleItem) capsuleWorld = capsuleT3d.inclusiveMatrix() assertVectorAlmostEqual(self, capsuleWorldPre, matrixToList(capsuleWorld), places=6) # Using setMatrixCmd() on a common transform API prim will set # translate, rotate, and scale. self.assertEqual( capsuleXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray(("xformOp:translate", "xformOp:translate:pivot", "xformOp:rotateXYZ", "xformOp:scale", "!invert!xformOp:translate:pivot")))
def testMultiSelectMoveUSD(self): '''Move multiple USD objects, read through Transform3d interface.''' # Select multiple balls to move them. proxyShapePathSegment = mayaUtils.createUfePathSegment( "|transform1|proxyShape1") balls = ['Ball_33', 'Ball_34'] ballPaths = [ ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/Room_set/Props/' + ball) ]) for ball in balls ] ballItems = [ ufe.Hierarchy.createItem(ballPath) for ballPath in ballPaths ] for ballItem in ballItems: ufe.GlobalSelection.get().append(ballItem) # We compare the UFE translation with the USD run-time translation. To # obtain the full translation of USD scene items, we need to add the USD # translation to the Maya proxy shape translation. proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath( 0).node() proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj) def usdSceneItemTranslation(item): prim = usdUtils.getPrimFromSceneItem(item) if not prim.HasAttribute('xformOp:translate'): return proxyShapeXformFn.translation(om.MSpace.kTransform) else: return addVec( proxyShapeXformFn.translation(om.MSpace.kTransform), prim.GetAttribute('xformOp:translate').Get()) def ufeSceneItemTranslation(item): return transform3dTranslation(ufe.Transform3d.transform3d(item)) # Set up the callables that will retrieve the translation. self.runTimeTranslation = usdSceneItemTranslation self.ufeTranslation = ufeSceneItemTranslation # Give the tail item in the selection an initial translation that # is different, to catch bugs where the relative translation # incorrectly squashes any existing translation. backItem = ballItems[-1] backT3d = ufe.Transform3d.transform3d(backItem) initialTranslation = [-10, -20, -30] backT3d.translate(*initialTranslation) assertVectorAlmostEqual(self, ufeSceneItemTranslation(backItem), usdSceneItemTranslation(backItem)) # Save the initial positions to the memento list. expected = [ usdSceneItemTranslation(ballItem) for ballItem in ballItems ] self.runMultiSelectTestMove(ballItems, expected)
def testRenameRestrictionSameLayerDef(self): '''Restrict renaming USD node. Cannot rename a prim defined on another layer.''' # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|transform1|proxyShape1') usdPathSegment = usdUtils.createUfePathSegment( '/Room_set/Props/Ball_35') ball35Path = ufe.Path([mayaPathSegment, usdPathSegment]) ball35Item = ufe.Hierarchy.createItem(ball35Path) ufe.GlobalSelection.get().append(ball35Item) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer()) self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # expect the exception happens with self.assertRaises(RuntimeError): newName = 'Ball_35_Renamed' cmds.rename(newName)
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)
def hierarchyBefore(): aPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/A')]) a = ufe.Hierarchy.createItem(aPath) bPath = aPath + ufe.PathComponent('B') b = ufe.Hierarchy.createItem(bPath) cPath = bPath + ufe.PathComponent('C') c = ufe.Hierarchy.createItem(cPath) ePath = dPath + ufe.PathComponent('E') e = ufe.Hierarchy.createItem(ePath) fPath = ePath + ufe.PathComponent('F') f = ufe.Hierarchy.createItem(fPath) gPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/G')]) g = ufe.Hierarchy.createItem(gPath) return [a, b, c, e, f, g]
def getItem(path): '''Get the UFE scene item and USD prim for an item under a USD path''' fullPath = ufe.Path([ mayaUtils.createUfePathSegment("|transform1|proxyShape1"), usdUtils.createUfePathSegment(path) ]) item = ufe.Hierarchy.createItem(fullPath) prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(fullPath)) return item, prim
def hierarchyBefore(): retVal = [] aPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/A')]) retVal.append(ufe.Hierarchy.createItem(aPath)) for i in range(4): aPath = aPath + ufe.PathComponent('A') retVal.append(ufe.Hierarchy.createItem(aPath)) return retVal
def testRenameProxyShape(self): '''Rename proxy shape, UFE lookup should succeed.''' mayaSegment = mayaUtils.createUfePathSegment("|transform1|proxyShape1") usdSegment = usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ball35Path = ufe.Path([mayaSegment, usdSegment]) ball35PathStr = ','.join( [str(segment) for segment in ball35Path.segments]) # Because of difference in Python binding systems (pybind11 for UFE, # Boost Python for mayaUsd and USD), need to pass in strings to # mayaUsd functions. Multi-segment UFE paths need to have # comma-separated segments. def assertStageAndPrimAccess(proxyShapeSegment, primUfePathStr, primSegment): proxyShapePathStr = str(proxyShapeSegment) stage = mayaUsd.ufe.getStage(proxyShapePathStr) prim = mayaUsd.ufe.ufePathToPrim(primUfePathStr) stagePath = mayaUsd.ufe.stagePath(stage) self.assertIsNotNone(stage) self.assertEqual(stagePath, proxyShapePathStr) self.assertTrue(prim.IsValid()) self.assertEqual(str(prim.GetPath()), str(primSegment)) assertStageAndPrimAccess(mayaSegment, ball35PathStr, usdSegment) # Rename the proxy shape node itself. Stage and prim access should # still be valid, with the new path. mayaSegment = mayaUtils.createUfePathSegment("|transform1|potato") cmds.rename('|transform1|proxyShape1', 'potato') self.assertEqual(len(cmds.ls('potato')), 1) ball35Path = ufe.Path([mayaSegment, usdSegment]) ball35PathStr = ','.join( [str(segment) for segment in ball35Path.segments]) assertStageAndPrimAccess(mayaSegment, ball35PathStr, usdSegment)
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)
def testRenameSpecialCharacter(self): # open twoSpheres.ma scene in testSamples mayaUtils.openTwoSpheresScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|usdSphereParent|usdSphereParentShape') usdPathSegment = usdUtils.createUfePathSegment('/sphereXform/sphere') basePath = ufe.Path([mayaPathSegment, usdPathSegment]) usdSphereItem = ufe.Hierarchy.createItem(basePath) ufe.GlobalSelection.get().append(usdSphereItem) # 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()) # rename with special chars newNameWithSpecialChars = '!@#%$@$=sph^e.re_*()<>}021|' cmds.rename(newNameWithSpecialChars) # get the prim pSphereItem = ufe.GlobalSelection.get().front() usdPrim = stage.GetPrimAtPath(str(pSphereItem.path().segments[1])) self.assertTrue(usdPrim) # prim names are not allowed to have special characters except '_' regex = re.compile('[@!#$%^&*()<>?/\|}{~:]') self.assertFalse(regex.search(usdPrim.GetName())) # rename starting with digits. newNameStartingWithDigit = '09123Potato' self.assertFalse(Tf.IsValidIdentifier(newNameStartingWithDigit)) cmds.rename(newNameStartingWithDigit) # get the prim pSphereItem = ufe.GlobalSelection.get().front() usdPrim = stage.GetPrimAtPath(str(pSphereItem.path().segments[1])) self.assertTrue(usdPrim) # prim names are not allowed to start with digits newValidName = Tf.MakeValidIdentifier(newNameStartingWithDigit) self.assertEqual(usdPrim.GetName(), newValidName)
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 testEditRouter(self): '''Test edit router functionality.''' cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer # Create the following hierarchy: # # ps # |_ A # |_ B # # We A and duplicate it. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() stage.DefinePrim('/A', 'Xform') stage.DefinePrim('/A/B', 'Xform') psPath = ufe.PathString.path(psPathStr) psPathSegment = psPath.segments[0] aPath = ufe.Path([psPathSegment, usdUtils.createUfePathSegment('/A')]) a = ufe.Hierarchy.createItem(aPath) bPath = aPath + ufe.PathComponent('B') b = ufe.Hierarchy.createItem(bPath) # Add a sub-layer, where the parent edit should write to. subLayerId = cmds.mayaUsdLayerEditor(stage.GetRootLayer().identifier, edit=True, addAnonymous="aSubLayer")[0] mayaUsd.lib.registerEditRouter('duplicate', firstSubLayer) sn = ufe.GlobalSelection.get() sn.clear() sn.append(a) cmds.duplicate() sublayer01 = Sdf.Find(subLayerId) self.assertIsNotNone(sublayer01) self.assertIsNotNone(sublayer01.GetPrimAtPath('/A1/B'))
def testRenameNotifications(self): '''Rename a USD node and test for the UFE notifications.''' # open usdCylinder.ma scene in testSamples mayaUtils.openCylinderScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|mayaUsdTransform|shape') usdPathSegment = usdUtils.createUfePathSegment('/pCylinder1') cylinderPath = ufe.Path([mayaPathSegment, usdPathSegment]) cylinderItem = ufe.Hierarchy.createItem(cylinderPath) ufe.GlobalSelection.get().append(cylinderItem) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # set the edit target to the root layer stage.SetEditTarget(stage.GetRootLayer()) self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # Create our UFE notification observer ufeObs = TestObserver() # We start off with no observers self.assertFalse(ufe.Scene.hasObserver(ufeObs)) # Add the UFE observer we want to test ufe.Scene.addObserver(ufeObs) # rename newName = 'pCylinder1_Renamed' cmds.rename(newName) # After the rename we should have 1 rename notif and no unexepected notifs. self.assertEqual(ufeObs.notifications(), 1) self.assertFalse(ufeObs.receivedUnexpectedNotif())
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 testMoveMaya(self): '''Move Maya object, read through the Transform3d interface.''' # Give the sphere an initial position, and select it. expected = om.MVector(1, 2, 3) sphereObj = om.MSelectionList().add('pSphere1').getDagPath(0).node() sphereFn = om.MFnTransform(sphereObj) sphereFn.setTranslation(expected, om.MSpace.kTransform) 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.runTimeTranslation = partial(sphereFn.translation, om.MSpace.kTransform) self.ufeTranslation = partial(transform3dTranslation, transform3d) self.runTestMove(expected)
def testRenameUniqueName(self): # open tree.ma scene in testSamples 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/trunk') trunkPath = ufe.Path([mayaPathSegment, usdPathSegment]) trunkItem = ufe.Hierarchy.createItem(trunkPath) ufe.GlobalSelection.get().append(trunkItem) # 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()) # rename `/TreeBase/trunk` to `/TreeBase/leavesXform` cmds.rename("leavesXform") # get the prim item = ufe.GlobalSelection.get().front() usdPrim = stage.GetPrimAtPath(str(item.path().segments[1])) self.assertTrue(usdPrim) # the new prim name is expected to be "leavesXform1" assert ([x for x in stage.Traverse()] == [ stage.GetPrimAtPath("/TreeBase"), stage.GetPrimAtPath("/TreeBase/leavesXform"), stage.GetPrimAtPath("/TreeBase/leavesXform/leaves"), stage.GetPrimAtPath("/TreeBase/leavesXform1"), ])
def checkParentDone(): # The xform now has the capsule as its child. xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertIn('Capsule1', childrenNames(xformChildren)) # Confirm that the capsule has not moved in world space. Must # re-create the prim and its scene item after path change. capsulePath = ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/Xform1/Capsule1') ]) capsulePrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(capsulePath)) capsuleXformable = UsdGeom.Xformable(capsulePrim) capsuleItem = ufe.Hierarchy.createItem(capsulePath) capsuleT3d = ufe.Transform3d.transform3d(capsuleItem) capsuleWorld = capsuleT3d.inclusiveMatrix() assertVectorAlmostEqual(self, capsuleWorldPre, matrixToList(capsuleWorld)) # No change in the capsule's transform ops. self.assertEqual(capsuleXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray(matrixOps)) # Matrix ops that were not targeted did not change. for checkMatrixOp in matrixOps: if checkMatrixOp == matrixOp: continue op = UsdGeom.XformOp( capsulePrim.GetAttribute(checkMatrixOp)) self.assertEqual( op.GetOpTransform( mayaUsd.ufe.getTime( ufe.PathString.string(capsulePath))), matrixValues[checkMatrixOp])
def testGroupKind(self): """ Tests that grouping maintains a contiguous model hierarchy when the parent of the group is in the model hierarchy. """ mayaPathSegment = mayaUtils.createUfePathSegment( "|transform1|proxyShape1") usdSegmentBall3 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_3") ball3Path = ufe.Path([mayaPathSegment, usdSegmentBall3]) ball3Item = ufe.Hierarchy.createItem(ball3Path) usdSegmentBall5 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_5") ball5Path = ufe.Path([mayaPathSegment, usdSegmentBall5]) ball5Item = ufe.Hierarchy.createItem(ball5Path) usdSegmentProps = usdUtils.createUfePathSegment("/Ball_set/Props") propsPath = ufe.Path([mayaPathSegment, usdSegmentProps]) propsItem = ufe.Hierarchy.createItem(propsPath) ufeSelection = ufe.GlobalSelection.get() ufeSelection.append(ball3Item) ufeSelection.append(ball5Item) groupName = "newGroup" cmds.group(name=groupName) newGroupPath = propsPath + ufe.PathComponent("%s1" % groupName) newGroupItem = ufe.Hierarchy.createItem(newGroupPath) newGroupPrim = usdUtils.getPrimFromSceneItem(newGroupItem) # The "Props" prim that was the parent of both "Ball" prims has # kind=group and is part of the model hierarchy, so the new group prim # should also have kind=group and be included in the model hierarchy. self.assertEqual( Usd.ModelAPI(newGroupPrim).GetKind(), Kind.Tokens.group) self.assertTrue(newGroupPrim.IsModel()) cmds.undo() # Clear the kind metadata on the "Props" prim before testing again. propsPrim = usdUtils.getPrimFromSceneItem(propsItem) Usd.ModelAPI(propsPrim).SetKind("") self.assertFalse(propsPrim.IsModel()) # Freshen the UFE scene items so they have valid handles to their # UsdPrims. ball3Item = ufe.Hierarchy.createItem(ball3Path) ball5Item = ufe.Hierarchy.createItem(ball5Path) ufeSelection.clear() ufeSelection.append(ball3Item) ufeSelection.append(ball5Item) cmds.group(name=groupName) newGroupItem = ufe.Hierarchy.createItem(newGroupPath) newGroupPrim = usdUtils.getPrimFromSceneItem(newGroupItem) # When the "Props" prim does not have an authored kind and is not part # of the model hierarchy, the new group prim should not have any kind # authored either. self.assertEqual(Usd.ModelAPI(newGroupPrim).GetKind(), "") self.assertFalse(newGroupPrim.IsModel())
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)
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 testRenameUndo(self): '''Rename USD node.''' # open usdCylinder.ma scene in testSamples mayaUtils.openCylinderScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|mayaUsdTransform|shape') usdPathSegment = usdUtils.createUfePathSegment('/pCylinder1') cylinderPath = ufe.Path([mayaPathSegment, usdPathSegment]) cylinderItem = ufe.Hierarchy.createItem(cylinderPath) cylinderHierarchy = ufe.Hierarchy.hierarchy(cylinderItem) propsItem = cylinderHierarchy.parent() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildrenPre = propsHierarchy.children() ufe.GlobalSelection.get().append(cylinderItem) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer()) self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # by default edit target is set to the Rootlayer. self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # rename cylinderItemType = cylinderItem.nodeType() newName = 'pCylinder1_Renamed' cmds.rename(newName) # The renamed item is in the selection. snIter = iter(ufe.GlobalSelection.get()) pCylinder1Item = next(snIter) pCylinder1RenName = str(pCylinder1Item.path().back()) self.assertEqual(pCylinder1RenName, newName) propsChildren = propsHierarchy.children() self.assertEqual(len(propsChildren), len(propsChildrenPre)) self.assertIn(pCylinder1Item, propsChildren) cmds.undo() self.assertEqual(cylinderItemType, ufe.GlobalSelection.get().back().nodeType()) def childrenNames(children): return [str(child.path().back()) for child in children] propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenNames = childrenNames(propsChildren) self.assertNotIn(pCylinder1RenName, propsChildrenNames) self.assertIn('pCylinder1', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre)) cmds.redo() self.assertEqual(cylinderItemType, ufe.GlobalSelection.get().back().nodeType()) propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenNames = childrenNames(propsChildren) self.assertIn(pCylinder1RenName, propsChildrenNames) self.assertNotIn('pCylinder1', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre))
def testDuplicate(self): '''Duplicate Maya and USD objects.''' # Select two objects, one Maya, one USD. spherePath = ufe.Path(mayaUtils.createUfePathSegment("|pSphere1")) sphereItem = ufe.Hierarchy.createItem(spherePath) sphereHierarchy = ufe.Hierarchy.hierarchy(sphereItem) worldItem = sphereHierarchy.parent() ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) ball35Hierarchy = ufe.Hierarchy.hierarchy(ball35Item) propsItem = ball35Hierarchy.parent() worldHierarchy = ufe.Hierarchy.hierarchy(worldItem) worldChildrenPre = worldHierarchy.children() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildrenPre = propsHierarchy.children() ufe.GlobalSelection.get().append(sphereItem) ufe.GlobalSelection.get().append(ball35Item) # Set the edit target to the layer in which Ball_35 is defined (has a # primSpec, in USD terminology). Otherwise, duplication will not find # a source primSpec to copy. Layers are the (anonymous) session layer, # the root layer, then the Assembly_room_set sublayer. Trying to find # the layer by name is not practical, as it requires the full path # name, which potentially differs per run-time environment. ball35Prim = usdUtils.getPrimFromSceneItem(ball35Item) stage = ball35Prim.GetStage() layer = stage.GetLayerStack()[2] stage.SetEditTarget(layer) cmds.duplicate() # The duplicate command doesn't return duplicated non-Maya UFE objects. # They are in the selection, in the same order as the sources. snIter = iter(ufe.GlobalSelection.get()) sphereDupItem = next(snIter) sphereDupName = str(sphereDupItem.path().back()) ball35DupItem = next(snIter) ball35DupName = str(ball35DupItem.path().back()) worldChildren = worldHierarchy.children() propsChildren = propsHierarchy.children() self.assertEqual(len(worldChildren) - len(worldChildrenPre), 1) self.assertEqual(len(propsChildren) - len(propsChildrenPre), 1) self.assertIn(sphereDupItem, worldChildren) self.assertIn(ball35DupItem, propsChildren) cmds.undo() # The duplicated items should no longer appear in the child list of # their parents. def childrenNames(children): return [str(child.path().back()) for child in children] worldHierarchy = ufe.Hierarchy.hierarchy(worldItem) worldChildren = worldHierarchy.children() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() worldChildrenNames = childrenNames(worldChildren) propsChildrenNames = childrenNames(propsChildren) self.assertNotIn(sphereDupName, worldChildrenNames) self.assertNotIn(ball35DupName, propsChildrenNames) # The duplicated items shoudl reappear after a redo cmds.redo() snIter = iter(ufe.GlobalSelection.get()) sphereDupItem = next(snIter) ball35DupItem = next(snIter) worldChildren = worldHierarchy.children() propsChildren = propsHierarchy.children() self.assertEqual(len(worldChildren) - len(worldChildrenPre), 1) self.assertEqual(len(propsChildren) - len(propsChildrenPre), 1) self.assertIn(sphereDupItem, worldChildren) self.assertIn(ball35DupItem, propsChildren) cmds.undo() # The duplicated items should not be assigned to the name of a # deactivated USD item. cmds.select(clear=True) # Delete the even numbered props: evenPropsChildrenPre = propsChildrenPre[0:35:2] for propChild in evenPropsChildrenPre: ufe.GlobalSelection.get().append(propChild) cmds.delete() worldHierarchy = ufe.Hierarchy.hierarchy(worldItem) worldChildren = worldHierarchy.children() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenPostDel = propsHierarchy.children() # Duplicate Ball_1 ufe.GlobalSelection.get().append(propsChildrenPostDel[0]) cmds.duplicate() snIter = iter(ufe.GlobalSelection.get()) ballDupItem = next(snIter) ballDupName = str(ballDupItem.path().back()) self.assertNotIn(ballDupItem, propsChildrenPostDel) self.assertNotIn(ballDupName, propsChildrenNames) self.assertEqual(ballDupName, "Ball_36") cmds.undo() # undo duplication cmds.undo() # undo deletion
def testRenameRestrictionVariant(self): '''Renaming prims inside a variantSet is not allowed.''' cmds.file(new=True, force=True) # open Variant.ma scene in testSamples mayaUtils.openVariantSetScene() # stage mayaPathSegment = mayaUtils.createUfePathSegment( '|Variant_usd|Variant_usdShape') stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # first check that we have a VariantSets objectPrim = stage.GetPrimAtPath('/objects') self.assertTrue(objectPrim.HasVariantSets()) # Geom usdPathSegment = usdUtils.createUfePathSegment('/objects/Geom') geomPath = ufe.Path([mayaPathSegment, usdPathSegment]) geomItem = ufe.Hierarchy.createItem(geomPath) geomPrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(geomPath)) # Small_Potato smallPotatoUsdPathSegment = usdUtils.createUfePathSegment( '/objects/Geom/Small_Potato') smallPotatoPath = ufe.Path( [mayaPathSegment, smallPotatoUsdPathSegment]) smallPotatoItem = ufe.Hierarchy.createItem(smallPotatoPath) smallPotatoPrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(smallPotatoPath)) # add Geom to selection list ufe.GlobalSelection.get().append(geomItem) # get prim spec for Geom prim primspecGeom = stage.GetEditTarget().GetPrimSpecForScenePath( geomPrim.GetPath()) # primSpec is expected to be None self.assertIsNone(primspecGeom) # rename "/objects/Geom" to "/objects/Geom_something" # expect the exception happens with self.assertRaises(RuntimeError): cmds.rename("Geom_something") # clear selection cmds.select(clear=True) # add Small_Potato to selection list ufe.GlobalSelection.get().append(smallPotatoItem) # get prim spec for Small_Potato prim primspecSmallPotato = stage.GetEditTarget().GetPrimSpecForScenePath( smallPotatoPrim.GetPath()) # primSpec is expected to be None self.assertIsNone(primspecSmallPotato) # rename "/objects/Geom/Small_Potato" to "/objects/Geom/Small_Potato_something" # expect the exception happens with self.assertRaises(RuntimeError): cmds.rename("Small_Potato_something")
def testUsdGroup(self): '''Creation of USD group objects.''' mayaPathSegment = mayaUtils.createUfePathSegment( "|transform1|proxyShape1") usdSegmentBall5 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_5") ball5Path = ufe.Path([mayaPathSegment, usdSegmentBall5]) ball5Item = ufe.Hierarchy.createItem(ball5Path) usdSegmentBall3 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_3") ball3Path = ufe.Path([mayaPathSegment, usdSegmentBall3]) ball3Item = ufe.Hierarchy.createItem(ball3Path) usdSegmentProps = usdUtils.createUfePathSegment("/Ball_set/Props") parentPath = ufe.Path([mayaPathSegment, usdSegmentProps]) parentItem = ufe.Hierarchy.createItem(parentPath) parentHierarchy = ufe.Hierarchy.hierarchy(parentItem) parentChildrenPre = parentHierarchy.children() self.assertEqual(len(parentChildrenPre), 6) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # set the edit target to balls.usda layer = stage.GetLayerStack()[1] self.assertEqual("ballset.usda", layer.GetDisplayName()) stage.SetEditTarget(layer) if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3000'): globalSn = ufe.GlobalSelection.get() globalSn.append(ball5Item) globalSn.append(ball3Item) # group groupName = cmds.group(ufe.PathString.string(ball5Path), ufe.PathString.string(ball3Path), n="newGroup") else: newGroupName = ufe.PathComponent("newGroup") ufeSelectionList = ufe.Selection() ufeSelectionList.append(ball5Item) ufeSelectionList.append(ball3Item) groupCmd = parentHierarchy.createGroupCmd(ufeSelectionList, newGroupName) groupCmd.execute() # Group object (a.k.a parent) will be added to selection list. This behavior matches the native Maya group command. globalSelection = ufe.GlobalSelection.get() groupPath = ufe.Path([ mayaPathSegment, usdUtils.createUfePathSegment("/Ball_set/Props/newGroup1") ]) self.assertEqual(globalSelection.front(), ufe.Hierarchy.createItem(groupPath)) # Group object (a.k.a parent) will be added to selection list. This behavior matches the native Maya group command. globalSelection = ufe.GlobalSelection.get() groupPath = ufe.Path([ mayaPathSegment, usdUtils.createUfePathSegment("/Ball_set/Props/newGroup1") ]) self.assertEqual(globalSelection.front(), ufe.Hierarchy.createItem(groupPath)) parentChildrenPost = parentHierarchy.children() self.assertEqual(len(parentChildrenPost), 5) # The command will now append a number 1 at the end to match the naming # convention in Maya. if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3000'): newGroupPath = parentPath + ufe.PathComponent(groupName) else: newGroupPath = parentPath + ufe.PathComponent("newGroup1") # Make sure the new group item has the correct Usd type newGroupItem = ufe.Hierarchy.createItem(newGroupPath) newGroupPrim = usdUtils.getPrimFromSceneItem(newGroupItem) newGroupType = newGroupPrim.GetTypeName() self.assertEqual(newGroupType, 'Xform') childPaths = set([child.path() for child in parentChildrenPost]) self.assertTrue(newGroupPath in childPaths) self.assertTrue(ball5Path not in childPaths) self.assertTrue(ball3Path not in childPaths) if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3000'): cmds.undo() else: groupCmd.undo() # global selection should not be empty after undo. if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3004'): self.assertEqual(len(globalSelection), 2) else: self.assertEqual(len(globalSelection), 1) parentChildrenUndo = parentHierarchy.children() self.assertEqual(len(parentChildrenUndo), 6) childPathsUndo = set([child.path() for child in parentChildrenUndo]) self.assertTrue(newGroupPath not in childPathsUndo) self.assertTrue(ball5Path in childPathsUndo) self.assertTrue(ball3Path in childPathsUndo) if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3000'): cmds.redo() else: groupCmd.redo() # global selection should still have the group path. self.assertEqual(globalSelection.front(), ufe.Hierarchy.createItem(groupPath)) parentChildrenRedo = parentHierarchy.children() self.assertEqual(len(parentChildrenRedo), 5) childPathsRedo = set([child.path() for child in parentChildrenRedo]) self.assertTrue(newGroupPath in childPathsRedo) self.assertTrue(ball5Path not in childPathsRedo) self.assertTrue(ball3Path not in childPathsRedo)
def testGroupRestriction(self): ''' Verify group restriction. ''' mayaPathSegment = mayaUtils.createUfePathSegment( "|transform1|proxyShape1") usdSegmentBall3 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_3") ball3Path = ufe.Path([mayaPathSegment, usdSegmentBall3]) ball3Item = ufe.Hierarchy.createItem(ball3Path) usdSegmentBall5 = usdUtils.createUfePathSegment( "/Ball_set/Props/Ball_5") ball5Path = ufe.Path([mayaPathSegment, usdSegmentBall5]) ball5Item = ufe.Hierarchy.createItem(ball5Path) usdSegmentProps = usdUtils.createUfePathSegment("/Ball_set/Props") propsPath = ufe.Path([mayaPathSegment, usdSegmentProps]) propsItem = ufe.Hierarchy.createItem(propsPath) ufeSelection = ufe.GlobalSelection.get() ufeSelection.append(ball3Item) ufeSelection.append(ball5Item) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # set the edit target to the session layer stage.SetEditTarget(stage.GetSessionLayer()) # expect the exception happens with self.assertRaises(RuntimeError): cmds.group(name="newGroup") # set the edit target to the root layer stage.SetEditTarget(stage.GetRootLayer()) # create a sphere stage.DefinePrim('/Sphere1', 'Sphere') # select the Sphere spherePath = ufe.PathString.path( "{},/Sphere1".format("|transform1|proxyShape1")) sphereItem = ufe.Hierarchy.createItem(spherePath) ufeSelection = ufe.GlobalSelection.get() ufeSelection.clear() ufeSelection.append(sphereItem) # set the edit target to the session layer stage.SetEditTarget(stage.GetSessionLayer()) # expect the exception happens. with self.assertRaises(RuntimeError): cmds.group(name="newGroup") # undo cmds.undo() # verify that group1 doesn't exist after the undo self.assertEqual([item for item in stage.Traverse()], [ stage.GetPrimAtPath("/Ball_set"), stage.GetPrimAtPath("/Ball_set/Props"), stage.GetPrimAtPath("/Ball_set/Props/Ball_1"), stage.GetPrimAtPath("/Ball_set/Props/Ball_2"), stage.GetPrimAtPath("/Ball_set/Props/Ball_3"), stage.GetPrimAtPath("/Ball_set/Props/Ball_4"), stage.GetPrimAtPath("/Ball_set/Props/Ball_5"), stage.GetPrimAtPath("/Ball_set/Props/Ball_6"), stage.GetPrimAtPath("/Sphere1") ])
def testGroupHierarchyWithRenaming(self): '''Grouping a node and a descendant when all descendants share the same name''' # MAYA-113532: when grouping a node and its descendant sharing the same name, we need to # detect the name conflicts and rename as we reparent into the group. cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer # Create the following hierarchy: # # ps # |_ A # |_ A # |_ A # |_ A # |_ A # # And group them all at the same level, which implies renaming. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() stage.DefinePrim('/A', 'Xform') stage.DefinePrim('/A/B', 'Cube') stage.DefinePrim('/A/A', 'Xform') stage.DefinePrim('/A/A/C', 'Cone') stage.DefinePrim('/A/A/A', 'Xform') stage.DefinePrim('/A/A/A/D', 'Sphere') stage.DefinePrim('/A/A/A/A', 'Xform') stage.DefinePrim('/A/A/A/A/E', 'Capsule') stage.DefinePrim('/A/A/A/A/A', 'Xform') stage.DefinePrim('/A/A/A/A/A/F', 'Cylinder') psPath = ufe.PathString.path(psPathStr) psPathSegment = psPath.segments[0] ps = ufe.Hierarchy.createItem(psPath) psHier = ufe.Hierarchy.hierarchy(ps) groupPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/group1')]) def hierarchyBefore(): retVal = [] aPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/A')]) retVal.append(ufe.Hierarchy.createItem(aPath)) for i in range(4): aPath = aPath + ufe.PathComponent('A') retVal.append(ufe.Hierarchy.createItem(aPath)) return retVal a, aa, aaa, aaaa, aaaaa = hierarchyBefore() def checkBefore(): # We care about 2 things: # - All Xforms are named A # - The non-xform stay in the right order: order = [("B", "Cube"), ("C", "Cone"), ("D", "Sphere"), ("E", "Capsule"), ("F", "Cylinder")] psChildren = psHier.children() self.assertEqual(len(psChildren), 1) a = psChildren[0] for gprimName, gprimTypeName in order: aHier = ufe.Hierarchy.hierarchy(a) aChildren = aHier.children() if gprimName == "F": self.assertEqual(len(aChildren), 1) else: self.assertEqual(len(aChildren), 2) for child in aChildren: prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(child.path())) if child.nodeName() == "A": self.assertEqual(prim.GetTypeName(), "Xform") a = child else: self.assertEqual(child.nodeName(), gprimName) self.assertEqual(prim.GetTypeName(), gprimTypeName) def checkAfter(): # We care about 2 things: # - Group has 5 Xform children. Don't care about names: they will be unique. # - Each child has a one of the non-xform prims. members = set([("B", "Cube"), ("C", "Cone"), ("D", "Sphere"), ("E", "Capsule"), ("F", "Cylinder")]) group = ufe.Hierarchy.createItem(groupPath) groupHier = ufe.Hierarchy.hierarchy(group) groupChildren = groupHier.children() self.assertEqual(len(groupChildren), 5) foundMembers = set() for child in groupChildren: prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(child.path())) self.assertEqual(prim.GetTypeName(), "Xform") childHier = ufe.Hierarchy.hierarchy(child) childChildren = childHier.children() self.assertEqual(len(childChildren), 1) member = childChildren[0] prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(member.path())) foundMembers.add((member.nodeName(), prim.GetTypeName())) self.assertEqual(members, foundMembers) sn = ufe.GlobalSelection.get() sn.clear() # We randomize the order a bit to make sure previously moved items do # not affect the next one. sn.append(a) sn.append(aaa) sn.append(aa) sn.append(aaaaa) sn.append(aaaa) cmds.group() checkAfter() cmds.undo() checkBefore() cmds.redo() checkAfter()
def testGroupHierarchy(self): '''Grouping a node and a descendant.''' # MAYA-112957: when grouping a node and its descendant, with the node # selected first, the descendant path becomes stale as soon as its # ancestor gets reparented. The Maya parent command must deal with # this. A similar test is done for parenting in testParentCmd.py cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer # Create the following hierarchy: # # ps # |_ A # |_ B # |_ C # |_ D # |_ E # |_ F # # |_ G # # We will select A, B, C, E, F and G, in order, and group. This will # create a new group under ps, with A, B, C, E, F and G as children. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() stage.DefinePrim('/A', 'Xform') stage.DefinePrim('/A/B', 'Xform') stage.DefinePrim('/A/B/C', 'Xform') stage.DefinePrim('/D', 'Xform') stage.DefinePrim('/D/E', 'Xform') stage.DefinePrim('/D/E/F', 'Xform') stage.DefinePrim('/G', 'Xform') psPath = ufe.PathString.path(psPathStr) psPathSegment = psPath.segments[0] ps = ufe.Hierarchy.createItem(psPath) psHier = ufe.Hierarchy.hierarchy(ps) dPath = ufe.Path([psPathSegment, usdUtils.createUfePathSegment('/D')]) d = ufe.Hierarchy.createItem(dPath) dHier = ufe.Hierarchy.hierarchy(d) groupPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/group1')]) def hierarchyBefore(): aPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/A')]) a = ufe.Hierarchy.createItem(aPath) bPath = aPath + ufe.PathComponent('B') b = ufe.Hierarchy.createItem(bPath) cPath = bPath + ufe.PathComponent('C') c = ufe.Hierarchy.createItem(cPath) ePath = dPath + ufe.PathComponent('E') e = ufe.Hierarchy.createItem(ePath) fPath = ePath + ufe.PathComponent('F') f = ufe.Hierarchy.createItem(fPath) gPath = ufe.Path( [psPathSegment, usdUtils.createUfePathSegment('/G')]) g = ufe.Hierarchy.createItem(gPath) return [a, b, c, e, f, g] def hierarchyAfter(): return [ ufe.Hierarchy.createItem(groupPath + ufe.PathComponent(pc)) for pc in ['A', 'B', 'C', 'E', 'F', 'G'] ] def checkBefore(a, b, c, e, f, g): psChildren = psHier.children() aHier = ufe.Hierarchy.hierarchy(a) bHier = ufe.Hierarchy.hierarchy(b) cHier = ufe.Hierarchy.hierarchy(c) eHier = ufe.Hierarchy.hierarchy(e) fHier = ufe.Hierarchy.hierarchy(f) gHier = ufe.Hierarchy.hierarchy(g) self.assertIn(a, psChildren) self.assertIn(d, psChildren) self.assertIn(g, psChildren) self.assertIn(b, aHier.children()) self.assertIn(c, bHier.children()) self.assertIn(e, dHier.children()) self.assertIn(f, eHier.children()) self.assertFalse(gHier.hasChildren()) def checkAfter(a, b, c, e, f, g): psChildren = psHier.children() self.assertNotIn(a, psChildren) self.assertIn(d, psChildren) self.assertNotIn(g, psChildren) group = ufe.Hierarchy.createItem(groupPath) groupHier = ufe.Hierarchy.hierarchy(group) groupChildren = groupHier.children() for child in [a, b, c, e, f, g]: hier = ufe.Hierarchy.hierarchy(child) self.assertFalse(hier.hasChildren()) self.assertIn(child, groupChildren) children = hierarchyBefore() checkBefore(*children) sn = ufe.GlobalSelection.get() sn.clear() for child in children: sn.append(child) cmds.group() children = hierarchyAfter() checkAfter(*children) cmds.undo() children = hierarchyBefore() checkBefore(*children) cmds.redo() children = hierarchyAfter() checkAfter(*children)