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 _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 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 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 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 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 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 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 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 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 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 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 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 testParentRelative(self): # Create scene items for the cube and the cylinder. shapeSegment = mayaUtils.createUfePathSegment( "|world|mayaUsdProxy1|mayaUsdProxyShape1") cubePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCube1")]) cubeItem = ufe.Hierarchy.createItem(cubePath) cylinderPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1")]) 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), 0) # 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,/pCube1", "|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1", relative=True) # Confirm that the cube is now a child of the cylinder. cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) self.assertIn("pCube1", childrenNames(cylChildren)) # Undo: the cube is no longer a child of the cylinder. cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 0) # Redo: confirm that the cube is again a child of the cylinder. cmds.redo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) self.assertIn("pCube1", 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("/pCylinder1/pCube1") ]) 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), 0)
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( "|world|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( "|world|mayaUsdProxy1|mayaUsdProxyShape1") cubePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCube1")]) cubeItem = ufe.Hierarchy.createItem(cubePath) cylinderPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1")]) 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), 0) # 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,/pCube1", "|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1") # Confirm that the cube is now a child of the cylinder. cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) self.assertIn("pCube1", childrenNames(cylChildren)) # Undo: the cube is no longer a child of the cylinder. cmds.undo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 0) # Redo: confirm that the cube is again a child of the cylinder. cmds.redo() cylChildren = cylHier.children() self.assertEqual(len(cylChildren), 1) self.assertIn("pCube1", 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("/pCylinder1/pCube1") ]) 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), 0)