def verifyEditedScene(): aMayaItem = ufe.GlobalSelection.get().front() aMayaPath = aMayaItem.path() aMayaPathStr = ufe.PathString.string(aMayaPath) # Confirm the hierarchy is preserved through the Hierarchy interface: # one child, the parent of the pulled item is the proxy shape, and # the proxy shape has the pulled item as a child, not the original USD # scene item. aMayaHier = ufe.Hierarchy.hierarchy(aMayaItem) self.assertEqual(len(aMayaHier.children()), 1) self.assertEqual(ps, aMayaHier.parent()) psHier = ufe.Hierarchy.hierarchy(ps) self.assertIn(aMayaItem, psHier.children()) self.assertNotIn(aUsdItem, psHier.children()) # Confirm the translation has been transferred, and that the local # transformation is only a translation. aDagPath = om.MSelectionList().add( ufe.PathString.string(aMayaPath)).getDagPath(0) aFn = om.MFnTransform(aDagPath) self.assertEqual(aFn.translation(om.MSpace.kObject), om.MVector(*xlation)) mayaMatrix = aFn.transformation().asMatrix() usdMatrix = xlateOp.GetOpTransform( mayaUsd.ufe.getTime(aUsdUfePathStr)) mayaValues = [v for v in mayaMatrix] usdValues = [v for row in usdMatrix for v in row] assertVectorAlmostEqual(self, mayaValues, usdValues)
def testMergeToUsdToNonRootTargetInSessionLayer(self): '''Merge edits on a USD transform back to USD targeting a non-root destination path that does not exists in the destination layer.''' # To merge back to USD, we must edit as Maya first. (ps, aXlateOp, _, aUsdUfePathStr, aUsdUfePath, aUsdItem, bXlateOp, _, bUsdUfePathStr, bUsdUfePath, bUsdItem) = \ createSimpleXformScene() with mayaUsd.lib.OpUndoItemList(): self.assertTrue( mayaUsd.lib.PrimUpdaterManager.editAsMaya(bUsdUfePathStr)) bMayaItem = ufe.GlobalSelection.get().front() (bMayaPath, bMayaPathStr, _, bMayaMatrix) = \ setMayaTranslation(bMayaItem, om.MVector(10, 11, 12)) psHier = ufe.Hierarchy.hierarchy(ps) # Merge edits back to USD. with mayaUsd.lib.OpUndoItemList(): stage = mayaUsd.ufe.getStage(bUsdUfePathStr) stage.SetEditTarget(stage.GetSessionLayer()) self.assertTrue( mayaUsd.lib.PrimUpdaterManager.mergeToUsd(bMayaPathStr)) # Check that edits have been preserved in USD. bUsdMatrix = bXlateOp.GetOpTransform( mayaUsd.ufe.getTime(bUsdUfePathStr)) mayaValues = [v for v in bMayaMatrix] usdValues = [v for row in bUsdMatrix for v in row] assertVectorAlmostEqual(self, mayaValues, usdValues)
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 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 verifyMergeToUsd(): # Edit will re-allocate anything that was under pulled prim due to deactivation # Grab new references for /A/B prim bUsdPrim = mayaUsd.ufe.ufePathToPrim(bUsdUfePathStr) bXlateOp = UsdGeom.Xformable(bUsdPrim).GetOrderedXformOps()[0] # Check that edits have been preserved in USD. for (usdUfePathStr, mayaMatrix, xlateOp) in \ zip([aUsdUfePathStr, bUsdUfePathStr], [aMayaMatrix, bMayaMatrix], [aXlateOp, bXlateOp]): usdMatrix = xlateOp.GetOpTransform( mayaUsd.ufe.getTime(usdUfePathStr)) mayaValues = [v for v in mayaMatrix] usdValues = [v for row in usdMatrix for v in row] assertVectorAlmostEqual(self, mayaValues, usdValues) # There no longer are any Maya to USD path mappings. for mayaPath in [aMayaPath, bMayaPath]: self.assertEqual(len(mayaToUsd.fromHost(mayaPath)), 0) # Hierarchy is restored: USD item is child of proxy shape, Maya item is # not. Be careful to use the Maya path rather than the Maya item, which # should no longer exist. self.assertIn(aUsdItem, psHier.children()) self.assertNotIn(aMayaPath, [child.path() for child in psHier.children()]) # Maya nodes are removed. for mayaPathStr in [aMayaPathStr, bMayaPathStr]: with self.assertRaises(RuntimeError): om.MSelectionList().add(mayaPathStr)
def snapshotRunTimeUFE(self): '''Return a pair with an op read from the run-time and from UFE. Tests that the op read from the run-time interface matches the UFE op. ''' # Get translation # rtAll = None ufeAll = None offset = 0 for op in self.ops: runTimeVec = self.runTimes[op]() ufeVec = self.ufes[op]() if op == self.rotate: # The runtimes for rotate return an MEulerRotation, which we # must convert to a vector in degrees, since updateTRS expects # it in that format. # r = runTimeVec.asVector() rtAll = self.updateTRS( rtAll, op, [degrees(r[0]), degrees(r[1]), degrees(r[2])] ) r = ufeVec.asVector() ufeAll = self.updateTRS( ufeAll, op, [degrees(r[0]), degrees(r[1]), degrees(r[2])] ) else: rtAll = self.updateTRS( rtAll, op, runTimeVec ) ufeAll = self.updateTRS( ufeAll, op, ufeVec ) assertVectorAlmostEqual(self, runTimeVec, ufeVec) assertVectorAlmostEqual(self, rtAll, ufeAll) return (rtAll, ufeAll)
def 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 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 assertRunTimeUFEAlmostEqual(self): '''Tests that the translation read from the run-time interface matches the UFE translation. Returns a pair with translation read from the run-time and from UFE. ''' runTimeVec = self.getRunTimeTranslation() ufeVec = self.getUfeTranslation() assertVectorAlmostEqual(self, runTimeVec, ufeVec)
def checkTransform(expectedTranslation): # Check through both USD and UFE interfaces assertVectorAlmostEqual(self, sphereT3d.translation().vector, expectedTranslation) expected = Gf.Matrix4d(1.0) expected.SetTranslate(expectedTranslation) actual = sphereXformable.GetLocalTransformation() self.assertTrue(Gf.IsClose(actual, expected, 1e-5))
def snapshotRunTimeUFE(self): '''Return a pair with scale read from the run-time and from UFE. Tests that the scale read from the run-time interface matches the UFE scale. ''' runTimeVec = self.runTimeScale() ufeVec = self.ufeScale() assertVectorAlmostEqual(self, runTimeVec, ufeVec, places=6) return (runTimeVec, ufeVec)
def snapShotAndTest(self, expected, places=7): '''Take a snapshot of the state and append it to the memento list. The snapshot is compared to the expected results. ''' snapshot = self.snapshotRunTimeUFE() self.memento.append(snapshot) # Since snapshotRunTimeUFE checks run-time to UFE equality, we can use # the UFE or the run-time value to check against expected. assertVectorAlmostEqual(self, snapshot[1], expected, places)
def snapshotRunTimeUFE(self): '''Return a pair with translation read from the run-time and from UFE. Tests that the translation read from the run-time interface matches the UFE translation. ''' runTimeVec = self.runTimeTranslation() ufeVec = self.ufeTranslation() assertVectorAlmostEqual(self, runTimeVec, ufeVec) return (runTimeVec, ufeVec)
def checkWorldSpaceXform(testCase, objects): '''Confirm matching Maya and UFE object world space positions. The Maya object is the first object in the objects argument, and is used as the benchmark.''' mayaWorld = cmds.xform(objects[0], q=True, ws=True, matrix=True) for t3d in objects[1:]: # Flatten out UFE matrices for comparison with Maya output. usdWorld = [y for x in t3d.inclusiveMatrix().matrix for y in x] assertVectorAlmostEqual(testCase, mayaWorld, usdWorld)
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 testTransformEditAsMaya(self): '''Edit a USD transform as a Maya object.''' (ps, xlateOp, xlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, _, _, _, _, _) = createSimpleXformScene() # Edit aPrim as Maya data. with mayaUsd.lib.OpUndoItemList(): self.assertTrue( mayaUsd.lib.PrimUpdaterManager.canEditAsMaya(aUsdUfePathStr)) self.assertTrue( mayaUsd.lib.PrimUpdaterManager.editAsMaya(aUsdUfePathStr)) # Test the path mapping services. # # Unfortunately, toHost is unimplemented as of 20-Sep-2021. Use the # selection to retrieve the Maya object. # # usdToMaya = ufe.PathMappingHandler.pathMappingHandler(aUsdItem) # aMayaUfePath = usdToMaya.toHost(aUsdUfePath) # self.assertEqual(aMayaUfePath.nbSegments(), 1) aMayaItem = ufe.GlobalSelection.get().front() aMayaPath = aMayaItem.path() self.assertEqual(aMayaPath.nbSegments(), 1) mayaToUsd = ufe.PathMappingHandler.pathMappingHandler(aMayaItem) self.assertEqual(mayaToUsd.fromHost(aMayaPath), aUsdUfePath) # Confirm the hierarchy is preserved through the Hierarchy interface: # one child, the parent of the pulled item is the proxy shape, and # the proxy shape has the pulled item as a child, not the original USD # scene item. aMayaHier = ufe.Hierarchy.hierarchy(aMayaItem) self.assertEqual(len(aMayaHier.children()), 1) self.assertEqual(ps, aMayaHier.parent()) psHier = ufe.Hierarchy.hierarchy(ps) self.assertIn(aMayaItem, psHier.children()) self.assertNotIn(aUsdItem, psHier.children()) # Confirm the translation has been transferred, and that the local # transformation is only a translation. aDagPath = om.MSelectionList().add( ufe.PathString.string(aMayaPath)).getDagPath(0) aFn = om.MFnTransform(aDagPath) self.assertEqual(aFn.translation(om.MSpace.kObject), om.MVector(*xlation)) mayaMatrix = aFn.transformation().asMatrix() usdMatrix = xlateOp.GetOpTransform(mayaUsd.ufe.getTime(aUsdUfePathStr)) mayaValues = [v for v in mayaMatrix] usdValues = [v for row in usdMatrix for v in row] assertVectorAlmostEqual(self, mayaValues, usdValues)
def multiSelectSnapshotRunTimeUFE(self, items): '''Return a list of pairs with scale read from the run-time and from UFE. Tests that the scale read from the run-time interface matches the UFE scale. ''' snapshot = [] for item in items: runTimeVec = self.runTimeScale(item) ufeVec = self.ufeScale(item) assertVectorAlmostEqual(self, runTimeVec, ufeVec, places=6) snapshot.append((runTimeVec, ufeVec)) return snapshot
def multiSelectSnapShotAndTest(self, items, expected, places=7): '''Take a snapshot of the state and append it to the memento list. The snapshot is compared to the expected results. ''' snapshot = self.multiSelectSnapshotRunTimeUFE(items) self.memento.append(snapshot) for (itemSnapshot, itemExpected) in zip(snapshot, expected): # Since multiSelectSnapshotRunTimeUFE checks run-time to UFE # equality, we can use the UFE or the run-time value to # check against expected. assertVectorAlmostEqual(self, itemSnapshot[1], itemExpected, places)
def multiSelectSnapshotRunTimeUFE(self, items): '''Return a list of pairs with translation read from the run-time and from UFE. Tests that the translation read from the run-time interface matches the UFE translation. ''' snapshot = [] for item in items: runTimeVec = self.runTimeTranslation(item) ufeVec = self.ufeTranslation(item) assertVectorAlmostEqual(self, runTimeVec, ufeVec) snapshot.append((runTimeVec, ufeVec)) return snapshot
def checkPivotsAndCompensations(testCase, mayaObjName, usdT3d): '''Confirm matching Maya and UFE object pivots and pivot compensations.''' # getAttr() returns a single-element vector that holds a 3-element tuple. assertVectorAlmostEqual(testCase, cmds.getAttr(mayaObjName+".rp")[0], usdT3d.rotatePivot().vector, places=6) assertVectorAlmostEqual(testCase, cmds.getAttr(mayaObjName+".sp")[0], usdT3d.scalePivot().vector, places=6) assertVectorAlmostEqual(testCase, cmds.getAttr(mayaObjName+".rpt")[0], usdT3d.rotatePivotTranslation().vector, places=6) assertVectorAlmostEqual(testCase, cmds.getAttr(mayaObjName+".spt")[0], usdT3d.scalePivotTranslation().vector, places=6)
def testTransform3dMatrixOpAccessors(self): '''Matrix transform op TRS must match separate transform op TRS.''' testFile = testUtils.getTestScene("transform3d", "TranslateRotate_vs_xform.usda") (psPath, stage) = mayaUtils.createProxyFromFile(testFile) # cam1 has the "transform" transform op (i.e. a matrix), and cam2 has # translate + rotateXYZ transform ops. cam1 = ufe.Hierarchy.createItem( ufe.PathString.path(psPath + ",/cameras/cam1")) cam2 = ufe.Hierarchy.createItem( ufe.PathString.path(psPath + ",/cameras/cam2")) cam1t3d = ufe.Transform3d.transform3d(cam1) cam2t3d = ufe.Transform3d.transform3d(cam2) cam1T = cam1t3d.translation() cam2T = cam2t3d.translation() testUtils.assertVectorAlmostEqual(self, cam1T.vector, cam2T.vector) cam1R = cam1t3d.rotation() cam2R = cam2t3d.rotation() testUtils.assertVectorAlmostEqual(self, cam1R.vector, cam2R.vector)
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 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 testParentRelative(self): # Create scene items for the cube and the cylinder. shapeSegment = mayaUtils.createUfePathSegment( "|mayaUsdProxy1|mayaUsdProxyShape1") cubePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cubeXform")]) cubeItem = ufe.Hierarchy.createItem(cubePath) cylinderPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cylinderXform")]) cylinderItem = ufe.Hierarchy.createItem(cylinderPath) # get the USD stage stage = mayaUsd.ufe.getStage(str(shapeSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # The cube is not a child of the cylinder. cylHier = ufe.Hierarchy.hierarchy(cylinderItem) cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) # Get the components of the cube's local transform. cubeT3d = ufe.Transform3d.transform3d(cubeItem) cubeT = cubeT3d.translation() cubeR = cubeT3d.rotation() cubeS = cubeT3d.scale() # Parent cube to cylinder in relative mode, using UFE path strings. cmds.parent("|mayaUsdProxy1|mayaUsdProxyShape1,/cubeXform", "|mayaUsdProxy1|mayaUsdProxyShape1,/cylinderXform", relative=True) # Confirm that the cube is now a child of the cylinder. cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 2) self.assertIn("cubeXform", childrenNames(cylChildren)) # Undo: the cube is no longer a child of the cylinder. cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) # Redo: confirm that the cube is again a child of the cylinder. cmds.redo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 2) self.assertIn("cubeXform", childrenNames(cylChildren)) # Confirm that the cube's local transform has not changed. Must # re-create the item, as its path has changed. cubeChildPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cylinderXform/cubeXform")]) cubeChildItem = ufe.Hierarchy.createItem(cubeChildPath) cubeChildT3d = ufe.Transform3d.transform3d(cubeChildItem) self.assertEqual(cubeChildT3d.translation(), cubeT) self.assertEqual(cubeChildT3d.rotation(), cubeR) self.assertEqual(cubeChildT3d.scale(), cubeS) # Move the parent ufe.GlobalSelection.get().append(cylinderItem) cmds.move(0, 10, 0, relative=True) # Do the same on the equivalent Maya objects. cmds.parent("pCube1", "pCylinder1", relative=True) cmds.move(0, 10, 0, "pCylinder1", relative=True) # Get its world space transform. cubeWorld = cubeChildT3d.inclusiveMatrix() cubeWorldList = matrixToList(cubeWorld) # Do the same for the equivalent Maya object. mayaCubeWorld = cmds.xform( "|pCylinder1|pCube1", q=True, ws=True, m=True) # Equivalent Maya and USD objects must have the same world transform. assertVectorAlmostEqual(self, cubeWorldList, mayaCubeWorld) # Undo to bring scene to its original state. for i in range(4): cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1)
def testParentAbsoluteMultiMatrixOp(self): """Test parent -absolute on prim with a transform stack with multiple matrix ops.""" cmds.file(new=True, force=True) # Create a scene with an xform and a capsule. import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePathStr = '|stage1|stageShape1' stage = mayaUsd.lib.GetPrim(proxyShapePathStr).GetStage() xformPrim = stage.DefinePrim('/Xform1', 'Xform') capsulePrim = stage.DefinePrim('/Capsule1', 'Capsule') xformXformable = UsdGeom.Xformable(xformPrim) capsuleXformable = UsdGeom.Xformable(capsulePrim) proxyShapePathSegment = mayaUtils.createUfePathSegment( proxyShapePathStr) # Translate and rotate the xform. xformPath = ufe.Path( [proxyShapePathSegment, usdUtils.createUfePathSegment('/Xform1')]) xformItem = ufe.Hierarchy.createItem(xformPath) sn = ufe.GlobalSelection.get() sn.clear() sn.append(xformItem) cmds.move(0, -5, 0, r=True, os=True, wd=True) cmds.rotate(0, -90, 0, r=True, os=True, fo=True) self.assertEqual( xformXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray(("xformOp:translate", "xformOp:rotateXYZ"))) sn.clear() capsulePath = ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/Capsule1') ]) capsuleItem = ufe.Hierarchy.createItem(capsulePath) # Add 3 matrix transform ops to the capsule. # matrix A: tx 5, ry 90 # matrix B: ty 10, rz 90 # matrix C: tz 15, rx 90 op = capsuleXformable.AddTransformOp(opSuffix='A') matrixValA = Gf.Matrix4d(0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 5, 0, 0, 1) op.Set(matrixValA) op = capsuleXformable.AddTransformOp(opSuffix='B') matrixValB = Gf.Matrix4d(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 10, 0, 1) op.Set(matrixValB) op = capsuleXformable.AddTransformOp(opSuffix='C') matrixValC = Gf.Matrix4d(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 15, 1) op.Set(matrixValC) matrixOps = [ "xformOp:transform:A", "xformOp:transform:B", "xformOp:transform:C" ] matrixValues = { "xformOp:transform:A": matrixValA, "xformOp:transform:B": matrixValB, "xformOp:transform:C": matrixValC } self.assertEqual(capsuleXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray(matrixOps)) # Capture the current world space transform of the capsule. capsuleT3d = ufe.Transform3d.transform3d(capsuleItem) capsuleWorldPre = matrixToList(capsuleT3d.inclusiveMatrix()) # We will run the parenting test 3 times, targeting each matrix op in # turn. for matrixOp in matrixOps: os.environ['MAYA_USD_MATRIX_XFORM_OP_NAME'] = matrixOp # The xform currently has no children. xformHier = ufe.Hierarchy.hierarchy(xformItem) xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 0) # Parent the capsule to the xform. cmds.parent(ufe.PathString.string(capsulePath), ufe.PathString.string(xformPath)) 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]) checkParentDone() # Undo: the xform no longer has a child, the capsule is still where # it has always been. cmds.undo() xformChildren = xformHier.children() self.assertEqual(len(xformChildren), 0) capsulePath = ufe.Path([ proxyShapePathSegment, usdUtils.createUfePathSegment('/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)) self.assertEqual(capsuleXformable.GetXformOpOrderAttr().Get(), Vt.TokenArray(matrixOps)) # Redo: capsule still hasn't moved. cmds.redo() checkParentDone() # Go back to initial conditions for next iteration of loop. cmds.undo()
def testParentToProxyShape(self): # Load a file with a USD hierarchy at least 2-levels deep. with OpenFileCtx("simpleHierarchy.ma"): # Create scene items for the proxy shape and the sphere. shapeSegment = mayaUtils.createUfePathSegment( "|mayaUsdProxy1|mayaUsdProxyShape1") shapePath = ufe.Path([shapeSegment]) shapeItem = ufe.Hierarchy.createItem(shapePath) spherePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1/pCube1/pSphere1")]) sphereItem = ufe.Hierarchy.createItem(spherePath) # 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 proxy shape. shapeHier = ufe.Hierarchy.hierarchy(shapeItem) shapeChildren = shapeHier.children() self.assertNotIn("pSphere1", childrenNames(shapeChildren)) # Get its world space transform. sphereT3d = ufe.Transform3d.transform3d(sphereItem) sphereWorld = sphereT3d.inclusiveMatrix() sphereWorldListPre = matrixToList(sphereWorld) # Parent sphere to proxy shape in absolute mode (default), using UFE # path strings.Expect the exception happens cmds.parent("|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1/pCube1/pSphere1", "|mayaUsdProxy1|mayaUsdProxyShape1") # Confirm that the sphere is now a child of the proxy shape. shapeChildren = shapeHier.children() self.assertIn("pSphere1", childrenNames(shapeChildren)) # Undo: the sphere is no longer a child of the proxy shape. cmds.undo() shapeChildren = shapeHier.children() self.assertNotIn("pSphere1", childrenNames(shapeChildren)) # Redo: confirm that the sphere is again a child of the proxy shape. cmds.redo() shapeChildren = shapeHier.children() self.assertIn("pSphere1", childrenNames(shapeChildren)) # Confirm that the sphere's world transform has not changed. Must # re-create the item, as its path has changed. sphereChildPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pSphere1")]) sphereChildItem = ufe.Hierarchy.createItem(sphereChildPath) sphereChildT3d = ufe.Transform3d.transform3d(sphereChildItem) sphereWorld = sphereChildT3d.inclusiveMatrix() assertVectorAlmostEqual( self, sphereWorldListPre, matrixToList(sphereWorld), places=6) # Undo. cmds.undo() shapeChildren = shapeHier.children() self.assertNotIn("pSphere1", childrenNames(shapeChildren))
def testParentAbsolute(self): # Create scene items for the cube and the cylinder. shapeSegment = mayaUtils.createUfePathSegment( "|mayaUsdProxy1|mayaUsdProxyShape1") cubePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cubeXform")]) cubeItem = ufe.Hierarchy.createItem(cubePath) cylinderPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cylinderXform")]) cylinderItem = ufe.Hierarchy.createItem(cylinderPath) # get the USD stage stage = mayaUsd.ufe.getStage(str(shapeSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # The cube is not a child of the cylinder. cylHier = ufe.Hierarchy.hierarchy(cylinderItem) cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) # Get its world space transform. cubeT3d = ufe.Transform3d.transform3d(cubeItem) cubeWorld = cubeT3d.inclusiveMatrix() cubeWorldListPre = matrixToList(cubeWorld) # Parent cube to cylinder in absolute mode (default), using UFE # path strings. cmds.parent("|mayaUsdProxy1|mayaUsdProxyShape1,/cubeXform", "|mayaUsdProxy1|mayaUsdProxyShape1,/cylinderXform") # Confirm that the cube is now a child of the cylinder. cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 2) self.assertIn("cubeXform", childrenNames(cylChildren)) # Undo: the cube is no longer a child of the cylinder. cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) # Redo: confirm that the cube is again a child of the cylinder. cmds.redo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 2) self.assertIn("cubeXform", childrenNames(cylChildren)) # Confirm that the cube's world transform has not changed. Must # re-create the item, as its path has changed. cubeChildPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/cylinderXform/cubeXform")]) cubeChildItem = ufe.Hierarchy.createItem(cubeChildPath) cubeChildT3d = ufe.Transform3d.transform3d(cubeChildItem) cubeWorld = cubeChildT3d.inclusiveMatrix() assertVectorAlmostEqual( self, cubeWorldListPre, matrixToList(cubeWorld), places=6) # Cube world y coordinate is currently 0. self.assertAlmostEqual(cubeWorld.matrix[3][1], 0) # Move the parent ufe.GlobalSelection.get().append(cylinderItem) cmds.move(0, 10, 0, relative=True) # Get the world space transform again. The y component should # have incremented by 10. cubeWorld = cubeChildT3d.inclusiveMatrix() self.assertAlmostEqual(cubeWorld.matrix[3][1], 10) # Undo everything. for i in range(2): cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1)
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 assertVectorAlmostEqual(self, ufeVector, usdVector): testUtils.assertVectorAlmostEqual(self, ufeVector.vector, usdVector)
def testMultiSelectScaleUSD(self): '''Scale multiple USD objects, read through Transform3d interface.''' # Select multiple balls to scale them. proxyShapePathSegment = mayaUtils.createUfePathSegment( "|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)