def setUp(self): ''' Called initially to set up the maya test environment ''' # Load plugins self.assertTrue(self.pluginsLoaded) # These tests requires no additional setup. if self._testMethodName in [ 'testAddNewPrim', 'testAddNewPrimWithDelete' ]: return # Open top_layer.ma scene in test-samples mayaUtils.openTopLayerScene() # Clear selection to start off. ufe.GlobalSelection.get().clear() # Select Ball_35. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) self.ball35Item = ufe.Hierarchy.createItem(ball35Path) self.ball35Prim = usdUtils.getPrimFromSceneItem(self.ball35Item) ufe.GlobalSelection.get().append(self.ball35Item) # Create a ContextOps interface for it. self.contextOps = ufe.ContextOps.contextOps(self.ball35Item)
def testAttributes(self): '''Engine method to run attributes test.''' # Get a UFE scene item for one of the balls in the scene. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) # Then create the attributes interface for that item. ball35Attrs = ufe.Attributes.attributes(ball35Item) self.assertIsNotNone(ball35Attrs) # Test that we get the same scene item back. self.assertEqual(ball35Item, ball35Attrs.sceneItem()) # Verify that ball35 contains the visibility attribute. self.assertTrue(ball35Attrs.hasAttribute(UsdGeom.Tokens.visibility)) # Verify the attribute type of 'visibility' which we know is a enum token. self.assertEqual(ball35Attrs.attributeType(UsdGeom.Tokens.visibility), ufe.Attribute.kEnumString) # Get all the attribute names for this item. ball35AttrNames = ball35Attrs.attributeNames # Visibility should be in this list. self.assertIn(UsdGeom.Tokens.visibility, ball35AttrNames)
def testMoveUSD(self): '''Move USD object, read through the Transform3d interface.''' # Select Ball_35 to move it. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|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 testMultiSelectMoveUSD(self): '''Move multiple USD objects, read through Transform3d interface.''' # Select multiple balls to move them. proxyShapePathSegment = mayaUtils.createUfePathSegment( "|world|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( '|world|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 setUp(self): ''' Called initially to set up the maya test environment ''' # Load plugins self.assertTrue(self.pluginsLoaded) # Open top_layer.ma scene in test-samples mayaUtils.openTopLayerScene() # Create a proxy shape with empty stage to start with. import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() # Clear selection to start off. ufe.GlobalSelection.get().clear() # Select Ball_35. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) self.ball35Item = ufe.Hierarchy.createItem(ball35Path) ufe.GlobalSelection.get().append(self.ball35Item) # Create a ContextOps interface for it. self.contextOps = ufe.ContextOps.contextOps(self.ball35Item)
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 test-samples mayaUtils.openAppleBiteScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|world|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 testScaleUSD(self): '''Scale USD object, read through the Transform3d interface.''' # Select Ball_35 to scale it. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|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 scale with the USD run-time scale. To # obtain the full scale of Ball_35, we need to add the USD # scale to the Maya proxy shape scale. proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath(0).node() proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj) 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 scale. self.runTimeScale = ball35Scale self.ufeScale = partial(transform3dScale, transform3d) # Save the initial position to the memento list. expected = ball35Scale() self.runTestScale(expected)
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) def setMayaTranslation(xlation): sphereFn.setTranslation(xlation, om.MSpace.kTransform) setMayaTranslation(expected) 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 set and get the translation. self.setRunTimeTranslation = setMayaTranslation self.getRunTimeTranslation = partial(sphereFn.translation, om.MSpace.kTransform) self.getUfeTranslation = partial(transform3dTranslation, transform3d) self.runTestMove()
def testRenameRestrictionOtherLayerOpinions(self): '''Restrict renaming USD node. Cannot rename a prim with definitions or opinions on other layers.''' # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|world|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)) # set the edit target to Assembly_room_set.usda stage.SetEditTarget(stage.GetLayerStack()[2]) self.assertEqual(stage.GetEditTarget().GetLayer().GetDisplayName(), "Assembly_room_set.usda") # expect the exception happens with self.assertRaises(RuntimeError): newName = 'Ball_35_Renamed' cmds.rename(newName)
def testRenameUniqueName(self): # open tree.ma scene in test-samples mayaUtils.openTreeScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment('|world|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 _testMultiSelectRotateUSD(self): '''Rotate multiple USD objects, read through Transform3d interface.''' # Select multiple balls to rotate them. proxyShapePathSegment = mayaUtils.createUfePathSegment( "|world|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 rotation with the USD run-time rotation. To # obtain the full rotation of USD scene items, we need to add the USD # rotation to the Maya proxy shape rotation. proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath(0).node() proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj) def usdSceneItemRotation(item): prim = usdUtils.getPrimFromSceneItem(item) if not prim.HasAttribute('xformOp:rotateXYZ'): return proxyShapeXformFn.rotation(om.MSpace.kTransform) else: x,y,z = prim.GetAttribute('xformOp:rotateXYZ').Get() return proxyShapeXformFn.rotation(om.MSpace.kTransform) + om.MEulerRotation(radians(x), radians(y), radians(z)) def ufeSceneItemRotation(item): return transform3dRotation(ufe.Transform3d.transform3d(item)) # Set up the callables that will retrieve the rotation. self.runTimeRotation = usdSceneItemRotation self.ufeRotation = ufeSceneItemRotation # Give the tail item in the selection an initial rotation that # is different, to catch bugs where the relative rotation # incorrectly squashes any existing rotation. backItem = ballItems[-1] backT3d = ufe.Transform3d.transform3d(backItem) initialRot = [-10, -20, -30] backT3d.rotate(*initialRot) assertVectorAlmostEqual(self, [radians(a) for a in initialRot], usdSceneItemRotation(backItem)) # Save the initial positions to the memento list. expected = [usdSceneItemRotation(ballItem) for ballItem in ballItems] # MAYA-96058: unfortunately, rotate command currently requires a rotate # manipulator to be created to update the UFE object. manipCtx = cmds.manipRotateContext() cmds.setToolTo(manipCtx) #Temporarily disabling undo redo until we fix it for PR 94 self.runMultiSelectTestRotate(ballItems, expected)
def testAlreadyChild(self): '''Parenting an object to its current parent is a no-op.''' with OpenFileCtx("simpleHierarchy.ma"): shapeSegment = mayaUtils.createUfePathSegment( "|world|mayaUsdProxy1|mayaUsdProxyShape1") spherePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1/pCube1/pSphere1")]) sphereItem = ufe.Hierarchy.createItem(spherePath) cylinderShapePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1/pCylinderShape1")]) cylinderShapeItem = ufe.Hierarchy.createItem(cylinderShapePath) parentPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1")]) parentItem = ufe.Hierarchy.createItem(parentPath) parent = ufe.Hierarchy.hierarchy(parentItem) childrenPre = parent.children() # get the USD stage stage = mayaUsd.ufe.getStage(str(shapeSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # The sphere is not a child of the cylinder self.assertNotIn("pSphere1", childrenNames(childrenPre)) # The cylinder shape is a child of the cylinder self.assertIn("pCylinderShape1", childrenNames(childrenPre)) # Parent the sphere and the cylinder shape to the cylinder. This # is a no-op for the cylinder shape, as it's already a child of the # cylinder, and the sphere is parented to the cylinder. cmds.parent(ufe.PathString.string(spherePath), ufe.PathString.string(cylinderShapePath), ufe.PathString.string(parentPath)) children = parent.children() self.assertEqual(len(childrenPre)+1, len(children)) self.assertIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children)) # Undo / redo cmds.undo() children = parent.children() self.assertEqual(len(childrenPre), len(children)) self.assertNotIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children)) cmds.redo() children = parent.children() self.assertEqual(len(childrenPre)+1, len(children)) self.assertIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children))
def testRename(self): # open tree.ma scene in test-samples mayaUtils.openTreeScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment('|world|Tree_usd|Tree_usdShape') usdPathSegment = usdUtils.createUfePathSegment('/TreeBase') treebasePath = ufe.Path([mayaPathSegment, usdPathSegment]) treebaseItem = ufe.Hierarchy.createItem(treebasePath) ufe.GlobalSelection.get().append(treebaseItem) # 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()) self.assertTrue(stage.GetRootLayer().GetPrimAtPath("/TreeBase")) # get default prim defaultPrim = stage.GetDefaultPrim() self.assertEqual(defaultPrim.GetName(), 'TreeBase') # TreeBase has two childern: leavesXform, trunk assert len(defaultPrim.GetChildren()) == 2 # get prim spec for defaultPrim primspec = stage.GetEditTarget().GetPrimSpecForScenePath(defaultPrim.GetPath()); # set primspec name primspec.name = "TreeBase_potato" # get the renamed prim renamedPrim = stage.GetPrimAtPath('/TreeBase_potato') # One must use the SdfLayer API for setting the defaultPrim when you rename the prim it identifies. stage.SetDefaultPrim(renamedPrim); # get defaultPrim again defaultPrim = stage.GetDefaultPrim() self.assertEqual(defaultPrim.GetName(), 'TreeBase_potato') # make sure we have a valid prims after the primspec rename assert stage.GetPrimAtPath('/TreeBase_potato') assert stage.GetPrimAtPath('/TreeBase_potato/leavesXform') # prim should be called TreeBase_potato potatoPrim = stage.GetPrimAtPath('/TreeBase_potato') self.assertEqual(potatoPrim.GetName(), 'TreeBase_potato') # prim should be called leaves leavesPrimSpec = stage.GetObjectAtPath('/TreeBase_potato/leavesXform/leaves') self.assertEqual(leavesPrimSpec.GetName(), 'leaves')
def testAnimatedBoundingBox(self): '''Test the Object3d bounding box interface for animated geometry.''' # Load up a scene with a sphere that has an animated radius, with # time connected to the proxy shape. filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test-samples", "sphereAnimatedRadius", "sphereAnimatedRadiusProxyShape.ma") cmds.file(filePath, force=True, open=True) # 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( '|world|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 xrange(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 testRename(self): '''Rename USD node.''' # Select a USD object. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) ball35Hierarchy = ufe.Hierarchy.hierarchy(ball35Item) propsItem = ball35Hierarchy.parent() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildrenPre = propsHierarchy.children() ufe.GlobalSelection.get().append(ball35Item) newName = 'Ball_35_Renamed' cmds.rename(newName) # The renamed item is in the selection. snIter = iter(ufe.GlobalSelection.get()) ball35RenItem = next(snIter) ball35RenName = str(ball35RenItem.path().back()) self.assertEqual(ball35RenName, newName) # MAYA-92350: should not need to re-bind hierarchy interface objects # with their item. propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() self.assertEqual(len(propsChildren), len(propsChildrenPre)) self.assertIn(ball35RenItem, propsChildren) cmds.undo() 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(ball35RenName, propsChildrenNames) self.assertIn('Ball_35', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre)) cmds.redo() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenNames = childrenNames(propsChildren) self.assertIn(ball35RenName, propsChildrenNames) self.assertNotIn('Ball_35', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre))
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) # Remove the test file. os.remove(usdFilePath)
def testRenameProxyShape(self): '''Rename proxy shape, UFE lookup should succeed.''' mayaSegment = mayaUtils.createUfePathSegment( "|world|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( "|world|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 testRenameNotifications(self): '''Rename a USD node and test for the UFE notifications.''' # open usdCylinder.ma scene in test-samples mayaUtils.openCylinderScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|world|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() if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '2021'): # We start off with no observers self.assertFalse(ufe.Scene.hasObjectAddObserver(ufeObs)) self.assertFalse(ufe.Scene.hasObjectDeleteObserver(ufeObs)) self.assertFalse(ufe.Scene.hasObjectPathChangeObserver(ufeObs)) # Add the UFE observers we want to test ufe.Scene.addObjectAddObserver(ufeObs) ufe.Scene.addObjectDeleteObserver(ufeObs) ufe.Scene.addObjectPathChangeObserver(ufeObs) else: # 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 testMultiSelectScaleUSD(self): '''Scale multiple USD objects, read through Transform3d interface.''' # Select multiple balls to scale them. proxyShapePathSegment = mayaUtils.createUfePathSegment( "|world|transform1|proxyShape1") # Test passes for a single item. # balls = ['Ball_33'] 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 scale with the USD run-time scale. To # obtain the full scale of USD scene items, we need to add the USD # scale to the Maya proxy shape scale. proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath(0).node() proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj) def usdSceneItemScale(item): prim = usdUtils.getPrimFromSceneItem(item) if not prim.HasAttribute('xformOp:scale'): return proxyShapeXformFn.scale() else: return combineScales(proxyShapeXformFn.scale(), prim.GetAttribute('xformOp:scale').Get()) def ufeSceneItemScale(item): return transform3dScale(ufe.Transform3d.transform3d(item)) # Set up the callables that will retrieve the scale. self.runTimeScale = usdSceneItemScale self.ufeScale = ufeSceneItemScale # Give the tail item in the selection an initial scale that # is different, to catch bugs where the relative scale # incorrectly squashes any existing scale. backItem = ballItems[-1] backT3d = ufe.Transform3d.transform3d(backItem) initialScale = [1.1, 2.2, 3.3] backT3d.scale(*initialScale) assertVectorAlmostEqual(self, initialScale, usdSceneItemScale(backItem), places=6) # Save the initial positions to the memento list. expected = [usdSceneItemScale(ballItem) for ballItem in ballItems] self.runMultiSelectTestScale(ballItems, expected, places=6)
def testVisibility(self): '''Test the Object3d visibility methods.''' # Open top_layer.ma scene in test-samples mayaUtils.openTopLayerScene() # Get a scene item for Ball_35. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|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 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 runTestAttribute(self, path, attrName, ufeAttrClass, ufeAttrType): '''Engine method to run attribute test.''' # Create the UFE/USD attribute for this test from the input path. # Get a UFE scene item the input path in the scene. itemPath = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment(path) ]) ufeItem = ufe.Hierarchy.createItem(itemPath) # Get the USD prim for this item. usdPrim = usdUtils.getPrimFromSceneItem(ufeItem) # Create the attributes interface for the item. ufeAttrs = ufe.Attributes.attributes(ufeItem) self.assertIsNotNone(ufeAttrs) # Get the USDAttribute for the input attribute name so we can use it to # compare to UFE. usdAttr = usdPrim.GetAttribute(attrName) self.assertIsNotNone(usdAttr) # Get the attribute that matches the input name and make sure it matches # the class type of UFE attribute class passed in. self.assertTrue(ufeAttrs.hasAttribute(attrName)) ufeAttr = ufeAttrs.attribute(attrName) self.assertIsInstance(ufeAttr, ufeAttrClass) # Verify that the attribute type matches the input UFE type. self.assertEqual(ufeAttr.type, ufeAttrType) # Verify that the scene item the attribute was created with matches # what is stored in the UFE attribute. self.assertEqual(ufeAttr.sceneItem(), ufeItem) # Verify that this attribute has a value. Note: all the attributes that # are tested by this method are assumed to have a value. self.assertTrue(ufeAttr.hasValue()) # Verify that the name matched what we created the attribute from. self.assertEqual(ufeAttr.name, attrName) # Test that the string representation of the value is not empty. self.assertTrue(str(ufeAttr)) return ufeAttr, usdAttr
def testObservation(self): '''Test Transform3d observation interface. As of 11-Apr-2018 only implemented for USD objects. ''' # Select Ball_35 to move it. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) # Create a Transform3d interface for it. transform3d = ufe.Transform3d.transform3d(ball35Item) t3dObs = TestObserver() # We start off with no observers for Ball_35. self.assertFalse(ufe.Transform3d.hasObservers(ball35Path)) self.assertFalse(ufe.Transform3d.hasObserver(ball35Item, t3dObs)) self.assertEqual(ufe.Transform3d.nbObservers(ball35Item), 0) # Set the observer to observe Ball_35. ufe.Transform3d.addObserver(ball35Item, t3dObs) self.assertTrue(ufe.Transform3d.hasObservers(ball35Path)) self.assertTrue(ufe.Transform3d.hasObserver(ball35Item, t3dObs)) self.assertEqual(ufe.Transform3d.nbObservers(ball35Item), 1) # No notifications yet. self.assertEqual(t3dObs.notifications(), 0) # We only select the ball AFTER doing our initial tests because # the MayaUSD plugin creates a transform3d observer on selection # change to update the bounding box. ufe.GlobalSelection.get().append(ball35Item) # Move the prim. ball35Prim = usdUtils.getPrimFromSceneItem(ball35Item) ball35Prim.GetAttribute('xformOp:translate').Set(Gf.Vec3d(10, 20, 30)) # Notified. self.assertEqual(t3dObs.notifications(), 1)
def testNodeTypes(self): '''Engine method to run scene item test.''' # Get a UFE scene item for one of the balls in the scene. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) # Ball35 is an Xform. ball35NodeType = ball35Item.nodeType() self.assertEqual(ball35NodeType, "Xform") # This node type should be the first item on the ancestor node type list # and it should have other items. ball35AncestorNodeTypes = ball35Item.ancestorNodeTypes() self.assertEqual(ball35NodeType, ball35AncestorNodeTypes[0]) self.assertTrue(len(ball35AncestorNodeTypes) > 1)
def testRotateUSD(self): '''Rotate USD object, read through the Transform3d interface.''' # Select Ball_35 to rotate it. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|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 rotation with the USD run-time rotation. To # obtain the full rotation of Ball_35, we need to add the USD # rotation to the Maya proxy shape rotation. proxyShapeXformObj = om.MSelectionList().add('transform1').getDagPath(0).node() proxyShapeXformFn = om.MFnTransform(proxyShapeXformObj) 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)) # Set up the callables that will retrieve the rotation. self.runTimeRotation = ball35Rotation self.ufeRotation = partial(transform3dRotation, transform3d) # Save the initial position to the memento list. expected = ball35Rotation() # MAYA-96058: unfortunately, rotate command currently requires a rotate # manipulator to be created to update the UFE object. manipCtx = cmds.manipRotateContext() cmds.setToolTo(manipCtx) #Temporarily disabling undo redo until we fix it for PR 94 self.runTestRotate(expected)
def testRenameSpecialCharacter(self): # open twoSpheres.ma scene in test-samples mayaUtils.openTwoSpheresScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|world|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())) # prim names are not allowed to start with digits newNameStartingWithDigit = '09123Potato' cmds.rename(newNameStartingWithDigit) self.assertFalse(usdPrim.GetName()[0].isdigit())
def testAddNewPrimWithDelete(self): cmds.file(new=True, force=True) # Create a proxy shape with empty stage to start with. import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() # Create a ContextOps interface for the proxy shape. proxyShapePath = ufe.Path( [mayaUtils.createUfePathSegment("|world|stage1|stageShape1")]) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) contextOps = ufe.ContextOps.contextOps(proxyShapeItem) # Add a new Xform prim. cmd = contextOps.doOpCmd(['Add New Prim', 'Xform']) self.assertIsNotNone(cmd) ufeCmd.execute(cmd) # The proxy shape should now have a single UFE child item. proxyShapehier = ufe.Hierarchy.hierarchy(proxyShapeItem) self.assertTrue(proxyShapehier.hasChildren()) self.assertEqual(len(proxyShapehier.children()), 1) # Using UFE, delete this new prim (which doesn't actually delete it but # instead makes it inactive). cmds.pickWalk(d='down') cmds.delete() # The proxy shape should now have no UFE child items (since we skip inactive). self.assertFalse(proxyShapehier.hasChildren()) self.assertEqual(len(proxyShapehier.children()), 0) # Add another Xform prim (which should get a unique name taking into # account the prim we just made inactive). cmd = contextOps.doOpCmd(['Add New Prim', 'Xform']) self.assertIsNotNone(cmd) ufeCmd.execute(cmd) # The proxy shape should now have a single UFE child item. self.assertTrue(proxyShapehier.hasChildren()) self.assertEqual(len(proxyShapehier.children()), 1)
def testScaleMaya(self): '''Scale Maya object, read through the Transform3d interface.''' # Give the sphere an initial position, and select it. expected = [1, 2, 3] sphereObj = om.MSelectionList().add('pSphere1').getDagPath(0).node() sphereFn = om.MFnTransform(sphereObj) sphereFn.setScale(expected) 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 scale. self.runTimeScale = partial(sphereFn.scale) self.ufeScale = partial(transform3dScale, transform3d) self.runTestScale(expected)
def testRotateMaya(self): '''Rotate Maya object, read through the Transform3d interface.''' # Give the sphere an initial position, and select it. rotation = om.MEulerRotation(radians(30), radians(60), radians(90)) sphereObj = om.MSelectionList().add('pSphere1').getDagPath(0).node() sphereFn = om.MFnTransform(sphereObj) sphereFn.setRotation(rotation, om.MSpace.kTransform) # Select pSphere1 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 rotation. self.runTimeRotation = partial( sphereFn.rotation, om.MSpace.kTransform) self.ufeRotation = partial(transform3dRotation, transform3d) self.runTestRotate(rotation)