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 testMayaGeomExtentsRecomputation(self): ''' Verify the automatic extents computation in when geom attributes change ''' cmds.file(new=True, force=True) # create a Capsule via contextOps menu import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) # capsule capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1') capsuleItem = ufe.Hierarchy.createItem(capsulePath) capsulePrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(capsulePath)) # get the height and extent "attributes" capsuleHeightAttr = capsulePrim.GetAttribute('height') capsuleExtentAttr = capsulePrim.GetAttribute('extent') self.assertAlmostEqual(capsuleHeightAttr.Get(), 1.0) capsuleExtent = capsuleExtentAttr.Get() expectedExtent = ((-0.5, -0.5, -1.0), (0.5, 0.5, 1.0)) self.assertTrue(almostEqualBBox(capsuleExtent, expectedExtent)) capsuleHeightAttr.Set(10.0) self.assertAlmostEqual(capsuleHeightAttr.Get(), 10.0) # Extent will have been recomputed: capsuleExtent = capsuleExtentAttr.Get() expectedExtent = ((-0.5, -0.5, -5.5), (0.5, 0.5, 5.5)) self.assertTrue(almostEqualBBox(capsuleExtent, expectedExtent))
def testSingleAttributeBlocking(self): ''' Authoring attribute(s) in weaker layer(s) are not permitted if there exist opinion(s) in stronger layer(s).''' # create new stage cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True) proxyShapePath = proxyShapes[0] proxyShapeItem = ufe.Hierarchy.createItem( ufe.PathString.path(proxyShapePath)) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage() # create a prim. This creates the primSpec in the root layer. proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) # create a USD prim capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1') capsuleItem = ufe.Hierarchy.createItem(capsulePath) capsulePrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(capsulePath)) # author a new radius value self.assertTrue(capsulePrim.HasAttribute('radius')) radiusAttrUsd = capsulePrim.GetAttribute('radius') # authoring new attribute edit is expected to be allowed. self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(radiusAttrUsd)) radiusAttrUsd.Set(10) # author a new axis value self.assertTrue(capsulePrim.HasAttribute('axis')) axisAttrUsd = capsulePrim.GetAttribute('axis') # authoring new attribute edit is expected to be allowed. self.assertTrue(mayaUsdUfe.isAttributeEditAllowed(axisAttrUsd)) axisAttrUsd.Set('Y') # create a sub-layer. rootLayer = stage.GetRootLayer() subLayerA = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="LayerA")[0] # check to see the if the sublayers was added addedLayers = [subLayerA] self.assertEqual(rootLayer.subLayerPaths, addedLayers) # set the edit target to LayerA. cmds.mayaUsdEditTarget(proxyShapePath, edit=True, editTarget=subLayerA) # radiusAttrUsd is not allowed to change since there is an opinion in a stronger layer self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(radiusAttrUsd)) # axisAttrUsd is not allowed to change since there is an opinion in a stronger layer self.assertFalse(mayaUsdUfe.isAttributeEditAllowed(axisAttrUsd))
def testCenterPivotUndo(self): cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1') capsuleItem = ufe.Hierarchy.createItem(capsulePath) usdT3d = ufe.Transform3d.transform3d(capsuleItem) sn = ufe.GlobalSelection.get() sn.clear() sn.append(capsuleItem) # center point is expected to be at [0.0, 0.0, 0.0] assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0]) # move the pivot location cmds.move(7, 2, 1, r=True, urp=True, usp=True) assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [7.0, 2.0, 1.0]) # call center pivot command cmds.xform(cp=True) # center point is expected to be at [0.0, 0.0, 0.0] assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0]) # undo cmds.undo() assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [7.0, 2.0, 1.0]) # redo cmds.redo() assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0])
def getCleanMayaStage(): """ gets a stage that only has an anon layer """ cmds.file(new=True, force=True) mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapes = cmds.ls(type=PROXY_NODE_TYPE, long=True) shapePath = proxyShapes[0] stage = mayaUsd.lib.GetPrim(shapePath).GetStage() return shapePath, stage
def testFallbackCases(self): '''Fallback handler test cases.''' cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Sphere']) spherePath = ufe.PathString.path('|stage1|stageShape1,/Sphere1') sphereItem = ufe.Hierarchy.createItem(spherePath) sphereT3d = ufe.Transform3d.transform3d(sphereItem) spherePrim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(spherePath)) sphereXformable = UsdGeom.Xformable(spherePrim) # Add transform ops that do not match either the Maya transform stack, # the USD common API transform stack, or a matrix stack. sphereXformable.AddTranslateOp() sphereXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat, "pivot") sphereXformable.AddRotateZOp() sphereXformable.AddTranslateOp(UsdGeom.XformOp.PrecisionFloat, "pivot", True) self.assertEqual( sphereXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray( ("xformOp:translate", "xformOp:translate:pivot", "xformOp:rotateZ", "!invert!xformOp:translate:pivot"))) self.assertFalse(UsdGeom.XformCommonAPI(sphereXformable)) self.assertFalse(mayaUsd.lib.XformStack.MayaStack().MatchingSubstack( sphereXformable.GetOrderedXformOps())) # Select sphere. sn = ufe.GlobalSelection.get() sn.clear() sn.append(sphereItem) # Rotate sphere around X. cmds.rotate(30, 0, 0, r=True, os=True, fo=True) # Fallback interface will have added a RotXYZ transform op. self.assertEqual( sphereXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray( ("xformOp:translate", "xformOp:translate:pivot", "xformOp:rotateZ", "!invert!xformOp:translate:pivot", "xformOp:rotateXYZ:maya_fallback")))
def testCreateStageWithNewLayer(self): # Create a proxy shape with empty stage to start with. import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() # Verify that we got a proxy shape object. mayaSel = cmds.ls(sl=True) self.assertEqual(1, len(mayaSel)) shapeNode = mayaSel[0] nt = cmds.nodeType(shapeNode) self.assertEqual('mayaUsdProxyShape', nt) # Verify that the shape node is connected to time. self.assertTrue(cmds.isConnected('time1.outTime', shapeNode + '.time'))
def testSessionLayer(self): '''Verify that the edit gets on the sessionLayer instead of the editTarget layer.''' import mayaUsd_createStageWithNewLayer proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() sessionLayer = stage.GetSessionLayer() prim = stage.DefinePrim('/A', 'Xform') primPathStr = proxyShapePathStr + ',/A' self.assertTrue(stage.GetSessionLayer().empty) with mayaUsd.lib.OpUndoItemList(): self.assertTrue( mayaUsd.lib.PrimUpdaterManager.canEditAsMaya(primPathStr)) self.assertTrue( mayaUsd.lib.PrimUpdaterManager.editAsMaya(primPathStr)) self.assertFalse(stage.GetSessionLayer().empty) kPullPrimMetadataKey = "Maya:Pull:DagPath" self.assertEqual(prim.GetCustomDataByKey(kPullPrimMetadataKey), "|__mayaUsd__|AParent|A") # Discard Maya edits, but there is nothing to discard. with mayaUsd.lib.OpUndoItemList(): self.assertTrue(mayaUsd.lib.PrimUpdaterManager.discardEdits("A")) self.assertTrue(stage.GetSessionLayer().empty) self.assertEqual(prim.GetCustomDataByKey(kPullPrimMetadataKey), None)
def testCustomRigUpdaterAutoEditLoad(self): "Validate auto edit on stage load for CustomRig codeless schema" import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsdUfe.getStage(proxyShape) self.assertTrue(stage) layer = stage.GetRootLayer() layer.ImportFromString(''' #sdf 1 ( defaultPrim = "world" ) def Xform "world" { def Xform "anim" { def CustomRig "bob" { int cubes = 2 bool autoEdit = 1 } } } ''') self.assertTrue(self._GetMFnDagNode("bob|pCube1")) self.assertTrue(self._GetMFnDagNode("bob|pCube2")) bobPrim = stage.GetPrimAtPath("/world/anim/bob") autoEditAttr = bobPrim.GetAttribute("autoEdit") self.assertTrue(autoEditAttr.Get())
def getCleanMayaStage(): """ gets a stage that only has an anon layer """ cmds.file(new=True, force=True) shapePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(shapePath).GetStage() return shapePath, stage
def createSimpleXformScene(): '''Create a simple scene with a trivial hierarchy: A translation (1, 2, 3) |_B translation (7, 8, 9) ''' psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() psPath = ufe.PathString.path(psPathStr) ps = ufe.Hierarchy.createItem(psPath) stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() aPrim = stage.DefinePrim('/A', 'Xform') aXformable = UsdGeom.Xformable(aPrim) aXlateOp = aXformable.AddTranslateOp() aXlation = Gf.Vec3d(1, 2, 3) aXlateOp.Set(aXlation) aUsdUfePathStr = psPathStr + ',/A' aUsdUfePath = ufe.PathString.path(aUsdUfePathStr) aUsdItem = ufe.Hierarchy.createItem(aUsdUfePath) bPrim = stage.DefinePrim('/A/B', 'Xform') bXformable = UsdGeom.Xformable(bPrim) bXlateOp = bXformable.AddTranslateOp() bXlation = Gf.Vec3d(7, 8, 9) bXlateOp.Set(bXlation) bUsdUfePathStr = aUsdUfePathStr + '/B' bUsdUfePath = ufe.PathString.path(bUsdUfePathStr) bUsdItem = ufe.Hierarchy.createItem(bUsdUfePath) return (ps, aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem)
def testDuplicateWithoutMaterials(self): '''Duplicate a Maya sphere without merging the materials.''' # Create a sphere. sphere = cmds.polySphere(r=1) # Create a stage to receive the USD duplicate. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() # Duplicate Maya data as USD data with materials cmds.mayaUsdDuplicate(cmds.ls(sphere, long=True)[0], psPathStr) # Verify that the copied sphere has a look (material) prim. looksPrim = stage.GetPrimAtPath("/pSphere1/Looks") self.assertTrue(looksPrim.IsValid()) # Undo duplicate to USD. cmds.undo() # Duplicate Maya data as USD data without materials cmds.mayaUsdDuplicate(cmds.ls(sphere, long=True)[0], psPathStr, exportOptions='shadingMode=none') # Verify that the copied sphere does not have a look (material) prim. looksPrim = stage.GetPrimAtPath("/pSphere1/Looks") self.assertFalse(looksPrim.IsValid())
def testDuplicateAsUsdSameName(self): '''Duplicate a Maya transform to USD when USD already has a prim with that name.''' # Create a Maya transform named 'A'. mayaA = cmds.createNode('transform', name='A') cmds.setAttr(mayaA + '.translate', 1, 2, 3) # Create a stage to receive the USD duplicate, with a prim of the same name. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() aPrim = stage.DefinePrim('/A', 'Xform') # Duplicate Maya data as USD data. As of 17-Nov-2021 no single-segment # path handler registered to UFE for Maya path strings, so use absolute # path. with mayaUsd.lib.OpUndoItemList(): self.assertTrue(mayaUsd.lib.PrimUpdaterManager.duplicate( cmds.ls(mayaA, long=True)[0], psPathStr)) # Maya hierarchy should be duplicated in USD, but with a numeric suffix due to the collision. usdNewAPathStr = psPathStr + ',/' + mayaA + '1' usdNewAPath = ufe.PathString.path(usdNewAPathStr) # Translations have been preserved. usdNewA = ufe.Hierarchy.createItem(usdNewAPath) usdNewAT3d = ufe.Transform3d.transform3d(usdNewA) self.assertEqual([1, 2, 3], usdNewAT3d.translation().vector)
def testBadSceneItem(self): '''Improperly constructed scene item should not crash Maya.''' # MAYA-112601 / GitHub #1169: improperly constructed Python scene item # should not cause a crash. cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Sphere']) spherePath = ufe.PathString.path('%s,/Sphere1' % proxyShape) # The proper way to create a scene item is the following: # # sphereItem = ufe.Hierarchy.createItem(spherePath) # # A naive user can create a scene item as the following. The resulting # scene item is not a USD scene item: it is a Python base class scene # item, which has a path but nothing else. This should not a crash # when using the move command. sphereItem = ufe.SceneItem(spherePath) sn = ufe.GlobalSelection.get() sn.clear() sn.append(sphereItem) cmds.move(0, 10, 0, relative=True, os=True, wd=True)
def setUp(self): # Start each test with a new scene with empty stage. cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer self.proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) self.stage = mayaUsd.lib.GetPrim(self.proxyShapePathStr).GetStage()
def testAnonymousRootToMaya(self): self.setupEmptyScene() import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) stage = mayaUsd.ufe.getStage(str(proxyShapePath)) newPrimPath = "/ChangeInRoot" stage.DefinePrim(newPrimPath, "xform") self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid()) stage.SetEditTarget(stage.GetSessionLayer()) newSessionsPrimPath = "/ChangeInSession" stage.DefinePrim(newSessionsPrimPath, "xform") self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid()) cmds.optionVar(intValue=('mayaUsd_SerializedUsdEditsLocation', 2)) cmds.file(save=True, force=True, type='mayaAscii') cmds.file(new=True, force=True) cmds.file(self._tempMayaFile, open=True) stage = mayaUsd.ufe.getStage('|stage1|stageShape1') self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid()) self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid()) shutil.rmtree(self._currentTestDir)
def testFactorySceneItem(self): '''A scene item can be constructed directly and will work.''' # Was: # MAYA-112601 / GitHub #1169: improperly constructed Python scene item # should not cause a crash. # Now: # MAYA-113409 / GutHub PR #222: Use factory CTOR for directly constructed # SceneItems. cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Sphere']) spherePath = ufe.PathString.path('%s,/Sphere1' % proxyShape) sphereItem = ufe.SceneItem(spherePath) sn = ufe.GlobalSelection.get() sn.clear() sn.append(sphereItem) cmds.move(0, 10, 0, relative=True, os=True, wd=True) # The USD prim will have a transform since the move command worked: sphereAttrs = ufe.Attributes.attributes(sphereItem) self.assertIsNotNone(sphereAttrs) self.assertTrue("xformOp:translate" in sphereAttrs.attributeNames)
def testPrimPropertyPathNotifs(self): import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) # Select the capsule capPath = ufe.PathString.path('%s,/Capsule1' % proxyShape) capItem = ufe.Hierarchy.createItem(capPath) ufe.GlobalSelection.get().clear() ufe.GlobalSelection.get().append(capItem) # No notifications yet. obs = TestObserver() ufe.Attributes.addObserver(capItem, obs) ufe.Transform3d.addObserver(capItem, obs) self.assertEqual(obs.notifications, 0) # Move the capsule cmds.move(0, 10, 10) # Verify that we got both ValueChanged and Transform3d notifs. # Note: we should get notifs on both the "xformOp:translate" and # "xformOpOrder" attributes. We don't care how many, just that # we are getting both of these notifs kinds on both the move # and undo. self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0) # Reset observer and then undo and again verify notifs. obs.reset() self.assertEqual(obs.notifications, 0) cmds.undo() self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0) # Reset and test same thing with Rotate. obs.reset() cmds.rotate(10, 0, 0) self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0) obs.reset() self.assertEqual(obs.notifications, 0) cmds.undo() self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0) # Reset and test same thing with Scale. obs.reset() cmds.scale(2, 2, 2) self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0) obs.reset() self.assertEqual(obs.notifications, 0) cmds.undo() self.assertTrue(obs.nbValueChanged > 0) self.assertTrue(obs.nbTransform3d > 0)
def testPurposeBoundingBox(self): '''Bounding box of prims with guide, proxy, and render purpose.''' # Create a scene with prims of purposes other than default: guide, # proxy, and render. All must have a valid bounding box. The bounding # box is conditional to the proxy shape on the UFE path to the prim # having that purpose enabled: if the purpose is disabled, the bounding # box is invalid. import mayaUsd_createStageWithNewLayer proxyShapePath = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) proxyShapePathSegment = mayaUtils.createUfePathSegment(proxyShapePath) stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage() usdPaths = ['/Cube1', '/Cube2', '/Cube3'] prims = [stage.DefinePrim(path, 'Cube') for path in usdPaths] purposes = [ UsdGeom.Tokens.proxy, UsdGeom.Tokens.guide, UsdGeom.Tokens.render ] for (prim, purpose) in zip(prims, purposes): imageable = UsdGeom.Imageable(prim) imageable.CreatePurposeAttr(purpose) # Create a UFE scene item for each prim, and get the bounding box using # the Object3d interface. for (prim, usdPath) in zip(prims, usdPaths): pathSegment = usdUtils.createUfePathSegment(usdPath) path = ufe.Path([proxyShapePathSegment, pathSegment]) item = ufe.Hierarchy.createItem(path) object3d = ufe.Object3d.object3d(item) # First turn off proxy, guide, render purposes on the proxy shape. # The bounding box should be invalid. purposeAttribs = [ 'drawProxyPurpose', 'drawGuidePurpose', 'drawRenderPurpose' ] for purposeAttrib in purposeAttribs: cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 0) bbox = object3d.boundingBox() self.assertTrue(bbox.empty()) # Next, turn on each purpose in turn on the proxy shape. The # bounding box should be valid only if the prim's purpose matches # the proxy shape purpose. imageable = UsdGeom.Imageable(prim) primPurpose = imageable.GetPurposeAttr().Get() for (purpose, purposeAttrib) in zip(purposes, purposeAttribs): cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 1) bbox = object3d.boundingBox() if primPurpose == purpose: assertVectorAlmostEqual(self, bbox.min.vector, [-1] * 3) assertVectorAlmostEqual(self, bbox.max.vector, [1] * 3) else: self.assertTrue(bbox.empty()) cmds.setAttr(proxyShapePath + '.' + purposeAttrib, 0)
def testDuplicateAsUsdUndoRedo(self): '''Duplicate a Maya transform hierarchy to USD and then undo and redo the command.''' # Create a hierarchy. Because group1 is selected upon creation, group2 # will be its parent. group1 = cmds.createNode('transform') group2 = cmds.group() self.assertEqual(cmds.listRelatives(group1, parent=True)[0], group2) cmds.setAttr(group1 + '.translate', 1, 2, 3) cmds.setAttr(group2 + '.translate', -4, -5, -6) # Create a stage to receive the USD duplicate. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() # Duplicate Maya data as USD data. As of 17-Nov-2021 no single-segment # path handler registered to UFE for Maya path strings, so use absolute # path. cmds.mayaUsdDuplicate(cmds.ls(group2, long=True)[0], psPathStr) def verifyDuplicate(): # Maya hierarchy should be duplicated in USD. usdGroup2PathStr = psPathStr + ',/' + group2 usdGroup1PathStr = usdGroup2PathStr + '/' + group1 usdGroup2Path = ufe.PathString.path(usdGroup2PathStr) usdGroup1Path = ufe.PathString.path(usdGroup1PathStr) # group1 is the child of group2 usdGroup1 = ufe.Hierarchy.createItem(usdGroup1Path) usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path) usdGroup1Hier = ufe.Hierarchy.hierarchy(usdGroup1) usdGroup2Hier = ufe.Hierarchy.hierarchy(usdGroup2) self.assertEqual(usdGroup2, usdGroup1Hier.parent()) self.assertEqual(len(usdGroup2Hier.children()), 1) self.assertEqual(usdGroup1, usdGroup2Hier.children()[0]) # Translations have been preserved. usdGroup1T3d = ufe.Transform3d.transform3d(usdGroup1) usdGroup2T3d = ufe.Transform3d.transform3d(usdGroup2) self.assertEqual([1, 2, 3], usdGroup1T3d.translation().vector) self.assertEqual([-4, -5, -6], usdGroup2T3d.translation().vector) verifyDuplicate() cmds.undo() def verifyDuplicateIsGone(): # Maya hierarchy should no longer be duplicated in USD. usdGroup2PathStr = psPathStr + ',/' + group2 usdGroup2Path = ufe.PathString.path(usdGroup2PathStr) usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path) self.assertIsNone(usdGroup2) verifyDuplicateIsGone() cmds.redo() verifyDuplicate()
def testZAttrChangeRedoAfterPrimCreateRedo(self): '''Redo attribute change after redo of prim creation.''' cmds.file(new=True, force=True) # Create a capsule, change one of its attributes. import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) cmd = proxyShapeContextOps.doOpCmd(['Add New Prim', 'Capsule']) ufeCmd.execute(cmd) capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1') capsuleItem = ufe.Hierarchy.createItem(capsulePath) # Create the attributes interface for the item. attrs = ufe.Attributes.attributes(capsuleItem) self.assertIsNotNone(attrs) self.assertTrue(attrs.hasAttribute('radius')) radiusAttr = attrs.attribute('radius') oldRadius = radiusAttr.get() ufeCmd.execute(radiusAttr.setCmd(2)) newRadius = radiusAttr.get() self.assertEqual(newRadius, 2) self.assertNotEqual(oldRadius, newRadius) # Undo 2x: undo attr change and prim creation. cmds.undo() cmds.undo() # Redo 2x: prim creation, attr change. cmds.redo() cmds.redo() # Re-create item, as its underlying prim was re-created. capsuleItem = ufe.Hierarchy.createItem(capsulePath) attrs = ufe.Attributes.attributes(capsuleItem) radiusAttr = attrs.attribute('radius') self.assertEqual(radiusAttr.get(), newRadius)
def testMayaShapeBBoxCacheClearing(self): ''' Verify that the bounding box cache gets cleared''' cmds.file(new=True, force=True) # create a Capsule via contextOps menu import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Xform']) xformPath = ufe.PathString.path('|stage1|stageShape1,/Xform1') xformItem = ufe.Hierarchy.createItem(xformPath) xformObject3d = ufe.Object3d.object3d(xformItem) proxyShapeContextOps = ufe.ContextOps.contextOps(xformItem) proxyShapeContextOps.doOp(['Add New Prim', 'Sphere']) proxyShapeContextOps.doOp(['Add New Prim', 'Sphere']) selectionList = OpenMaya.MSelectionList() selectionList.add('|stage1|stageShape1') shapeNode = OpenMaya.MFnDagNode(selectionList.getDependNode(0)) # Two spheres at origin, the bounding box is the unit cube: expectedBBox = ((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0)) self.assertTrue(almostEqualBBox(xformObject3d.boundingBox(), expectedBBox)) # Shape BBox should be the same (and will be cached): self.assertTrue(almostEqualBBox(shapeNode.boundingBox, expectedBBox)) sphere1Path = ufe.PathString.path('|stage1|stageShape1,/Xform1/Sphere1') sphere1Item = ufe.Hierarchy.createItem(sphere1Path) sphere1Prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(sphere1Path)) UsdGeom.XformCommonAPI(sphere1Prim).SetTranslate((-5, 0, 0)) sphere2Path = ufe.PathString.path('|stage1|stageShape1,/Xform1/Sphere2') sphere2Item = ufe.Hierarchy.createItem(sphere2Path) sphere2Prim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(sphere2Path)) UsdGeom.XformCommonAPI(sphere2Prim).SetTranslate((0, 5, 0)) expectedBBox = ((-6.0, -1.0, -1.0), (1.0, 6.0, 1.0)) self.assertTrue(almostEqualBBox(xformObject3d.boundingBox(), expectedBBox)) # The next test will only work if the cache was cleared when translating # the spheres: self.assertTrue(almostEqualBBox(shapeNode.boundingBox, expectedBBox))
def createLayer(self): ''' Helper that creates a stage and layer and return the item of its shape item. ''' import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) return proxyShapeItem, proxyShapePath
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 createStage(): ''' create a simple stage ''' cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True) proxyShapePath = proxyShapes[0] proxyShapeItem = ufe.Hierarchy.createItem( ufe.PathString.path(proxyShapePath)) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) stage = mayaUsd.lib.GetPrim(proxyShapePath).GetStage() return (stage, proxyShapePathStr, proxyShapeItem, proxyShapeContextOps)
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 testIllegalEditAsMaya(self): '''Trying to edit as Maya on object that doesn't support it.''' import mayaUsd_createStageWithNewLayer proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() blendShape = stage.DefinePrim('/BlendShape1', 'BlendShape') scope = stage.DefinePrim('/Scope1', 'Scope') blendShapePathStr = proxyShapePathStr + ',/BlendShape1' scopePathStr = proxyShapePathStr + ',/Scope1' # Blend shape cannot be edited as Maya: it has no importer. with mayaUsd.lib.OpUndoItemList(): self.assertFalse( mayaUsd.lib.PrimUpdaterManager.canEditAsMaya( blendShapePathStr)) self.assertFalse( mayaUsd.lib.PrimUpdaterManager.editAsMaya(blendShapePathStr))
def testAnonymousRootToUsd(self): self.setupEmptyScene() import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) stage = mayaUsd.ufe.getStage(str(proxyShapePath)) newPrimPath = "/ChangeInRoot" stage.DefinePrim(newPrimPath, "xform") self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid()) stage.SetEditTarget(stage.GetSessionLayer()) newSessionsPrimPath = "/ChangeInSession" stage.DefinePrim(newSessionsPrimPath, "xform") self.assertTrue(stage.GetPrimAtPath(newSessionsPrimPath).IsValid()) cmds.optionVar(intValue=('mayaUsd_SerializedUsdEditsLocation', 1)) msg = ("Session Layer before: " + stage.GetSessionLayer().identifier) stage = None cmds.file(save=True, force=True, type='mayaAscii') cmds.file(new=True, force=True) cmds.file(self._tempMayaFile, open=True) stage = mayaUsdLib.GetPrim('|stage1|stageShape1').GetStage() msg += (" Session Layer after: " + stage.GetSessionLayer().identifier) self.assertTrue(stage.GetPrimAtPath(newPrimPath).IsValid()) # Temporarily disabling this check while investigating why it can fail on certain build combinations # self.assertFalse(stage.GetPrimAtPath( # newSessionsPrimPath).IsValid(), msg) cmds.file(new=True, force=True) shutil.rmtree(self._currentTestDir)
def testMayaSelectSwitchVariant(self): '''Stale selection items must be removed on variant switch.''' import mayaUsd_createStageWithNewLayer import maya.internal.ufeSupport.ufeCmdWrapper as ufeCmd # Create a scene with two variants. proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() stage = mayaUsd.lib.GetPrim(proxyShape).GetStage() top = stage.DefinePrim('/Xform1', 'Xform') vset = top.GetVariantSets().AddVariantSet('modelingVariant') vset.AddVariant('cube') vset.AddVariant('sphere') vset.SetVariantSelection('cube') with vset.GetVariantEditContext(): stage.DefinePrim('/Xform1/Cube', 'Cube') vset.SetVariantSelection('sphere') with vset.GetVariantEditContext(): stage.DefinePrim('/Xform1/Sphere', 'Sphere') # The sphere is the sole child of Xform1. Get an attribute from it, # select it. xformPath = ufe.PathString.path('%s,/Xform1' % proxyShape) spherePath = ufe.PathString.path('%s,/Xform1/Sphere' % proxyShape) xformItem = ufe.Hierarchy.createItem(xformPath) sphereItem = ufe.Hierarchy.createItem(spherePath) xformHier = ufe.Hierarchy.hierarchy(xformItem) xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertEqual(xformChildren[0].path(), spherePath) sphereAttrs = ufe.Attributes.attributes(sphereItem) sphereRadius = sphereAttrs.attribute('radius') self.assertEqual(sphereRadius.get(), 1) sn = ufe.GlobalSelection.get() sn.clear() sn.append(sphereItem) self.assertEqual(len(sn), 1) # Switch variants using a command: the cube is now the sole child of # Xform1, we can get an attribute from the cube. The selection must # now be empty. xformCtxOps = ufe.ContextOps.contextOps(xformItem) cmd = xformCtxOps.doOpCmd(['Variant Sets', 'modelingVariant', 'cube']) ufeCmd.execute(cmd) cubePath = ufe.PathString.path('%s,/Xform1/Cube' % proxyShape) cubeItem = ufe.Hierarchy.createItem(cubePath) xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertEqual(xformChildren[0].path(), cubePath) cubeAttrs = ufe.Attributes.attributes(cubeItem) cubeRadius = cubeAttrs.attribute('size') self.assertEqual(cubeRadius.get(), 2) self.assertTrue(sn.empty()) # Undo: selection is restored, seletion item is valid. cmds.undo() self.assertEqual(len(sn), 1) sphereItem = sn.front() self.assertEqual(sphereItem.path(), spherePath) sphereAttrs = ufe.Attributes.attributes(sphereItem) sphereRadius = sphereAttrs.attribute('radius') self.assertEqual(sphereRadius.get(), 1) xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertEqual(xformChildren[0].path(), spherePath) # Redo: selection is cleared. cmds.redo() self.assertTrue(sn.empty()) xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 1) self.assertEqual(xformChildren[0].path(), cubePath) # Undo: selection restored to sphere. cmds.undo() self.assertEqual(len(sn), 1) sphereItem = sn.front() self.assertEqual(sphereItem.path(), spherePath) # Now set the variant outside a Maya command. Stale scene items must be # removed from the selection. vset.SetVariantSelection('cube') self.assertTrue(sn.empty())
def testMayaSelectMuteLayer(self): '''Stale selection items must be removed on mute layer.''' # Create new stage import mayaUsd_createStageWithNewLayer proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer( ) proxyShapePath = ufe.PathString.path(proxyShapePathStr) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) # Create a sub-layer. stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() rootLayer = stage.GetRootLayer() subLayerId = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="Layer1")[0] # Set the edit target to new sub-layer. cmds.mayaUsdEditTarget(proxyShapePathStr, edit=True, editTarget=subLayerId) # Create a prim. This will create the primSpec in the new sub-layer. proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) capsulePathStr = '%s,/Capsule1' % proxyShapePathStr capsulePath = ufe.PathString.path(capsulePathStr) capsuleItem = ufe.Hierarchy.createItem(capsulePath) # Select the prim. This is the core of the test: on subtree invalidate, # the prim's UFE scene item should be removed from the selection. sn = ufe.GlobalSelection.get() sn.clear() sn.append(capsuleItem) self.assertTrue(sn.contains(capsulePath)) # Mute sub-layer cmds.mayaUsdLayerEditor(subLayerId, edit=True, muteLayer=[1, proxyShapePathStr]) # Should be nothing on the selection list. self.assertTrue(sn.empty()) # Undo: capsule should be back on the selection list. cmds.undo() self.assertTrue(sn.contains(capsulePath)) # Redo: selection list now empty. cmds.redo() self.assertTrue(sn.empty()) cmds.undo() # Change attribute on the capsule, using the item from the selection, # which must be valid. self.assertTrue(len(sn), 1) capsuleItem = sn.front() capsuleAttrs = ufe.Attributes.attributes(capsuleItem) self.assertIsNotNone(capsuleAttrs) capsuleRadius = capsuleAttrs.attribute('radius') capsuleRadius.set(2) self.assertEqual(capsuleRadius.get(), 2) # Now mute the layer outside a Maya command. Stale scene items must be # removed from the selection. self.assertTrue(len(sn), 1) self.assertTrue(sn.contains(capsulePath)) stage.MuteLayer(subLayerId) self.assertTrue(sn.empty())