def testPerInstanceInheritedData(self): self._StartTest('perInstanceInheritedData') # These tests don't work in earlier versions of USD, the wrong # instance index gets selected if Usd.GetVersion() < (0, 20, 8): return # Hide and show some instances to make sure it updates correctly # These should start working correctly when MAYA-110508 is fixed stage = mayaUsdUfe.getStage("|stage|stageShape") ball_03_vis = stage.GetPrimAtPath('/root/group/ball_03').GetAttribute( 'visibility') ball_04_vis = stage.GetPrimAtPath('/root/group/ball_04').GetAttribute( 'visibility') cmds.select("|stage|stageShape,/root/group/ball_03") self.assertSnapshotClose('%s_ball_03_selected.png' % self._testName) ball_03_vis.Set('hidden') self.assertSnapshotClose('%s_ball_03_hidden.png' % self._testName) ball_04_vis.Set('hidden') self.assertSnapshotClose('%s_ball_03_and_04_hidden.png' % self._testName) ball_03_vis.Set('inherited') # this should show the object again self.assertSnapshotClose('%s_ball_04_hidden.png' % self._testName) ball_04_vis.Set('inherited') self.assertSnapshotClose('%s_shown_after_hidden.png' % self._testName) # These tests behave differently before USD version 21.05, so don't run # them for those earlier versions. if Usd.GetVersion() < (0, 21, 5): return # Modify the purpose of some instances to make sure they update correctly # These should start working correctly when MAYA-110170 is fixed ball_03_purpose = stage.GetPrimAtPath( '/root/group/ball_03').GetAttribute('purpose') ball_04_purpose = stage.GetPrimAtPath( '/root/group/ball_04').GetAttribute('purpose') ball_03_purpose.Set('guide') self.assertSnapshotClose('%s_ball_03_guide.png' % self._testName) ball_04_purpose.Set('guide') self.assertSnapshotClose('%s_ball_03_and_04_guide.png' % self._testName) ball_03_purpose.Set('default') self.assertSnapshotClose('%s_ball_04_guide.png' % self._testName) ball_03_purpose.Set('default') self.assertSnapshotClose('%s_default_after_guide.png' % self._testName)
def setUpClass(cls): inputPath = fixturesUtils.readOnlySetUpClass(__file__) cmds.file(new=True, force=True) # We need to use different input usd files depending on the version of USD. # We do this because USD introduced breaking changes on lights in USD 21.* # More precisely : # # - Before USD 21.* : lights attributes didn't have any prefix. Attributes # were named "intensity", "color", etc... # - In 21.02 : The attributes from light schemas were added a # namespace "inputs". But attributes from UsdLuxShapingAPI # (used for spot lights) or UsdLuxShadowsAPI (for shadows) # were still left without this namespace # - In 21.05 : Attributes from UsdLuxShadows and UsdLuxShapingAPI also # have a namespace "inputs" testFile = "LightsTest.usda" usdVersion = Usd.GetVersion() if usdVersion < (0, 21, 2): testFile = "LightsTest_2011.usda" elif usdVersion == (0, 21, 2): testFile = "LightsTest_2102.usda" # Import from USD. usdFilePath = os.path.join(inputPath, "UsdImportLightTest", testFile) cmds.usdImport(file=usdFilePath, readAnimData=True) cls._stage = Usd.Stage.Open(usdFilePath)
def test_SkelImport(self): cmds.file(new=True, force=True) path = os.path.join(self.inputPath, "UsdImportSkeleton", "skelCube.usda") cmds.usdImport(file=path, readAnimData=True, primPath="/Root", shadingMode=[ ["none", "default"], ]) stage = Usd.Stage.Open(path) skelCache = UsdSkel.Cache() bindingSitePrim = stage.GetPrimAtPath("/Root") self.assertTrue(bindingSitePrim.IsA(UsdSkel.Root)) if Usd.GetVersion() > (0, 20, 8): skelCache.Populate(UsdSkel.Root(bindingSitePrim), Usd.PrimDefaultPredicate) else: skelCache.Populate(UsdSkel.Root(bindingSitePrim)) skel = UsdSkel.Skeleton.Get(stage, "/Root/Skeleton") self.assertTrue(skel) skelQuery = skelCache.GetSkelQuery(skel) self.assertTrue(skelQuery) meshPrim = stage.GetPrimAtPath("/Root/Cube") self.assertTrue(meshPrim) skinningQuery = skelCache.GetSkinningQuery(meshPrim) self.assertTrue(skinningQuery) jointNames = [ name.split("/")[-1] for name in skelQuery.GetJointOrder() ] joints = [_GetDepNode(n) for n in jointNames] self.assertTrue(all(joints)) self._ValidateJointTransforms(skelQuery, joints) self._ValidateJointBindPoses(skelQuery, joints) self._ValidateBindPose("Skeleton_bindPose", skelQuery, joints) self._ValidateMeshTransform(meshPrim.GetName(), skinningQuery) self._ValidateSkinClusterRig(joints=joints, skinClusterName="skinCluster_{}".format( meshPrim.GetName()), groupPartsName="skinClusterGroupParts", groupIdName="skinClusterGroupId", bindPoseName="Skeleton_bindPose", meshName=meshPrim.GetName(), usdSkelQuery=skelQuery, usdSkinningQuery=skinningQuery)
def testExportPxrRisShading(self): """ Tests that exporting a Maya mesh with a simple Maya shading setup results in the correct shading on the USD mesh. """ cubePrim = self._stage.GetPrimAtPath('/MarbleCube/Geom/Cube') self.assertTrue(cubePrim) # Validate the Material prim bound to the Mesh prim. materialBindingAPI = UsdShade.MaterialBindingAPI(cubePrim) material = materialBindingAPI.ComputeBoundMaterial()[0] self.assertTrue(material) materialPath = material.GetPath().pathString self.assertEqual(materialPath, '/MarbleCube/Materials/MarbleCubeSG') if Usd.GetVersion() >= (0, 21, 5): # For USD 21.05 and later, GetInputs() and GetOutputs() take an # "onlyAuthored" argument that is True by default, so in that case # we expect only one output on the material for the "surface" # terminal in the "ri" renderContext that the export should have # authored. expectedNumOutputs = 1 else: # Otherwise prior to USD 21.05, GetInputs() and GetOutputs() did # not take any arguments and always included the built-in # terminals for the universal renderContext as well as any other # authored terminals. expectedNumOutputs = 4 # Validate the surface shader that is connected to the material. materialOutputs = material.GetOutputs() self.assertEqual(len(materialOutputs), expectedNumOutputs) print(self._stage.ExportToString()) materialOutput = material.GetOutput('ri:surface') (connectableAPI, outputName, outputType) = materialOutput.GetConnectedSource() self.assertEqual(outputName, 'out') shader = UsdShade.Shader(connectableAPI) self.assertTrue(shader) shaderId = shader.GetIdAttr().Get() self.assertEqual(shaderId, 'PxrMayaMarble') # Validate the connected input on the surface shader. shaderInput = shader.GetInput('placementMatrix') self.assertTrue(shaderInput) (connectableAPI, outputName, outputType) = shaderInput.GetConnectedSource() self.assertEqual(outputName, 'worldInverseMatrix') shader = UsdShade.Shader(connectableAPI) self.assertTrue(shader) shaderId = shader.GetIdAttr().Get() self.assertEqual(shaderId, 'PxrMayaPlacement3d')
def testPointInstancerGrid7k(self): mayaUtils.openPointInstancesGrid7kScene() cmds.FrameAllInAllViews() # Set the USD point instances pick mode to "PointInstancer" so that we pick # the instancer and not point instances instances during the test. cmds.optionVar(stringValue=( testUtilsSelectabilityPointInstanceSelection._pointInstancesPickModeOptionVarName, 'PointInstancer')) # In USD versions before 21.05, the point instancer pick mode did not exists. # For those version we end-up selecting the prototypes, of which there are 7. expectedCount = 1 if Usd.GetVersion() >= (0, 21, 2) else 7 self._RunTest(expectedCount)
def testNodeTypes(self): '''Engine method to run scene item test.''' # Get a UFE scene item for one of the balls in the scene. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) # Ball35 is an Xform. ball35NodeType = ball35Item.nodeType() self.assertEqual(ball35NodeType, "Xform") # This node type should be the first item on the ancestor node type list # and it should have other items. ball35AncestorNodeTypes = ball35Item.ancestorNodeTypes() if Usd.GetVersion() > (0, 20, 2): self.assertEqual(ball35NodeType, ball35AncestorNodeTypes[0]) else: self.assertEqual("UsdGeomXform", ball35AncestorNodeTypes[0]) self.assertTrue(len(ball35AncestorNodeTypes) > 1)
def testMayaReferenceAttributes(self): layer = Sdf.Layer.CreateAnonymous() stageOut = Usd.Stage.Open(layer.identifier) primPath = Sdf.AssetPath('/MayaReference') mayaReferencePath = '/somewherenice/path.ma' mayaNamespace = 'nsp' primOut = stageOut.DefinePrim(primPath.path, 'MayaReference') self.assertTrue(primOut.IsValid()) mayaReferenceOut = mayaUsdSchemas.MayaReference(primOut) self.assertTrue(mayaReferenceOut.GetPrim()) if Usd.GetVersion() > (0, 20, 2): typeName = Usd.SchemaRegistry().GetSchemaTypeName( mayaReferenceOut._GetStaticTfType()) else: typeName = mayaReferenceOut.GetSchemaClassPrimDefinition().typeName self.assertEqual(typeName, 'MayaReference') mayaReferenceAttr = primOut.CreateAttribute('mayaReference', Sdf.ValueTypeNames.Asset) mayaReferenceAttr.Set(mayaReferencePath) mayaNamespaceAttr = primOut.CreateAttribute('mayaNamespace', Sdf.ValueTypeNames.String) mayaNamespaceAttr.Set(mayaNamespace) stageIn = Usd.Stage.Open(stageOut.Flatten()) primIn = stageIn.GetPrimAtPath(primPath.path) self.assertTrue(primIn.IsValid()) mayaReferenceIn = mayaUsdSchemas.MayaReference(primIn) self.assertTrue(mayaReferenceIn.GetMayaReferenceAttr().Get(), mayaReferencePath) self.assertTrue(mayaReferenceIn.GetMayaNamespaceAttr().Get(), mayaNamespace)
def testSkelTransforms(self): """ Tests that the computed joint transforms in USD, when tarnsformed into world space, match the world space transforms of the Maya joints. """ mayaFile = os.path.join(self.inputPath, "UsdExportSkeletonTest", "UsdExportSkeleton.ma") cmds.file(mayaFile, force=True, open=True) # frameRange = [1, 30] frameRange = [1, 3] # TODO: The joint hierarchy intentionally includes non-joint nodes, # which are expected to be ignored. However, when we try to extract # restTransforms from the dagPose, the intermediate transforms cause # problems, since they are not members of the dagPose. As a result, # no dag pose is exported. Need to come up with a way to handle this # correctly in export. print("Expect warnings about invalid restTransforms") usdFile = os.path.abspath('UsdExportSkeleton.usda') cmds.usdExport(mergeTransformAndShape=True, file=usdFile, shadingMode='none', frameRange=frameRange, exportSkels='auto') stage = Usd.Stage.Open(usdFile) root = UsdSkel.Root.Get(stage, '/SkelChar') self.assertTrue(root) skelCache = UsdSkel.Cache() if Usd.GetVersion() > (0, 20, 8): skelCache.Populate(root, Usd.PrimDefaultPredicate) else: skelCache.Populate(root) skel = UsdSkel.Skeleton.Get(stage, '/SkelChar/Hips') self.assertTrue(skel) skelQuery = skelCache.GetSkelQuery(skel) self.assertTrue(skelQuery) xfCache = UsdGeom.XformCache() for frame in range(*frameRange): cmds.currentTime(frame, edit=True) xfCache.SetTime(frame) skelLocalToWorld = xfCache.GetLocalToWorldTransform( skelQuery.GetPrim()) usdJointXforms = skelQuery.ComputeJointSkelTransforms(frame) for joint, usdJointXf in zip(skelQuery.GetJointOrder(), usdJointXforms): usdJointWorldXf = usdJointXf * skelLocalToWorld selList = OM.MSelectionList() selList.add(Sdf.Path(joint).name) dagPath = selList.getDagPath(0) mayaJointWorldXf = Gf.Matrix4d(*dagPath.inclusiveMatrix()) self.assertTrue( Gf.IsClose(mayaJointWorldXf, usdJointWorldXf, 1e-5))
class testSchemaApiAdaptor(unittest.TestCase): @classmethod def setUpClass(cls): cls._rootPath = fixturesUtils.setUpClass(__file__) @classmethod def tearDownClass(cls): standalone.uninitialize() def setUp(self): pass @unittest.skipUnless(Usd.GetVersion() > (0, 21, 2), 'USD Lux becomes connectable in 21.05.') def testMinimalAdaptation(self): """Test that we can adapt ShadowAPI to an existing light shape. This exercises the most basic callbacks exposed by a SchemaAPI adaptor:""" mayaUsdLib.SchemaApiAdaptor.Register(shadowApiAdaptorShape, "light", "ShadowAPI") lightShape1 = cmds.pointLight() cmds.setAttr(lightShape1 + ".shadowColor", 0.5, 0.25, 0) lightShape2 = cmds.pointLight() cubeXform, cubeShape = cmds.polyCube() # Wrong type: not adapted adaptor = mayaUsdLib.Adaptor(cubeShape) self.assertEqual(adaptor.GetAppliedSchemas(), []) # Wrong name: not adapted adaptor = mayaUsdLib.Adaptor(lightShape2) self.assertEqual(adaptor.GetAppliedSchemas(), []) # Adapted: adaptor = mayaUsdLib.Adaptor(lightShape1) self.assertEqual(adaptor.GetAppliedSchemas(), ["ShadowAPI"]) schema = adaptor.GetSchemaByName("ShadowAPI") self.assertTrue(schema) self.assertEqual(set(schema.GetAuthoredAttributeNames()), set(["inputs:shadow:color", "inputs:shadow:enable"])) colorAttr = schema.GetAttribute("inputs:shadow:color") self.assertTrue(colorAttr) linearizedValue = (0.21763764, 0.047366142, 0) colorAttrValue = colorAttr.Get() self.assertAlmostEqual(linearizedValue[0], colorAttrValue[0]) self.assertAlmostEqual(linearizedValue[1], colorAttrValue[1]) self.assertEqual(linearizedValue[2], colorAttrValue[2]) colorAttr.Set((1, 0, 1)) self.assertEqual(cmds.getAttr(lightShape1 + ".shadowColor"), [(1.0, 0.0, 1.0)]) @unittest.skipUnless(HAS_BULLET, 'Requires the bullet plugin.') def testComplexAdaptation(self): """Test that we can adapt a bullet simulation""" mayaUsdLib.SchemaApiAdaptor.Register(TestBulletMassShemaAdaptor, "shape", "PhysicsMassAPI") mayaUsdLib.SchemaApiAdaptor.Register(TestBulletRigidBodyShemaAdaptor, "shape", "PhysicsRigidBodyAPI") # Build a scene (and exercise the adaptor in a freeform context) cmds.file(f=True, new=True) s1T = cmds.polySphere()[0] cmds.loadPlugin("bullet") if not BulletUtils.checkPluginLoaded(): return rbT, rbShape = RigidBody.CreateRigidBody().command( autoFit=True, colliderShapeType=RigidBody.eShapeType.kColliderSphere, meshes=[s1T], radius=1.0, mass=5.0, centerOfMass=(0.9, 0.8, 0.7)) # See if the plugin adaptor can read the bullet shape under the mesh: sl = om.MSelectionList() sl.add(s1T) dagPath = sl.getDagPath(0) dagPath.extendToShape() adaptor = mayaUsdLib.Adaptor(dagPath.fullPathName()) self.assertEqual(adaptor.GetUsdType(), Tf.Type.FindByName('UsdGeomMesh')) # NOTICE: PhysicsRigidBodyAPI is not in the list because that API is # supported only on export!!! self.assertEqual(adaptor.GetAppliedSchemas(), ['PhysicsMassAPI']) physicsMass = adaptor.GetSchemaByName("PhysicsMassAPI") self.assertEqual(physicsMass.GetName(), "PhysicsMassAPI") massAttributes = set([ 'physics:centerOfMass', 'physics:density', 'physics:diagonalInertia', 'physics:mass', 'physics:principalAxes' ]) self.assertEqual(set(physicsMass.GetAttributeNames()), massAttributes) bulletAttributes = set(['physics:centerOfMass', 'physics:mass']) self.assertEqual(set(physicsMass.GetAuthoredAttributeNames()), bulletAttributes) bulletMass = physicsMass.GetAttribute('physics:mass') self.assertAlmostEqual(bulletMass.Get(), 5.0) bulletMass.Set(12.0) bulletCenter = physicsMass.GetAttribute('physics:centerOfMass') bulletCenter.Set(Gf.Vec3f(3, 4, 5)) sl = om.MSelectionList() sl.add(s1T) bulletPath = sl.getDagPath(0) bulletPath.extendToShape(1) massDepFn = om.MFnDependencyNode(bulletPath.node()) plug = om.MPlug(bulletPath.node(), massDepFn.attribute("mass")) self.assertAlmostEqual(plug.asFloat(), 12.0) # Create an untranslated attribute: usdDensity = physicsMass.CreateAttribute('physics:density') usdDensity.Set(33.0) self.assertAlmostEqual(usdDensity.Get(), 33.0) # This will result in a dynamic attribute on the bulletShape: plug = massDepFn.findPlug("USD_ATTR_physics_density", True) self.assertAlmostEqual(plug.asFloat(), 33.0) bulletAttributes.add('physics:density') self.assertEqual(set(physicsMass.GetAuthoredAttributeNames()), bulletAttributes) physicsMass.RemoveAttribute('physics:density') bulletAttributes.remove('physics:density') self.assertEqual(set(physicsMass.GetAuthoredAttributeNames()), bulletAttributes) # Add some animation: cmds.setKeyframe(bulletPath, at="mass", t=0, v=3.0) cmds.setKeyframe(bulletPath, at="mass", t=10, v=30.0) # Modify the velocity so it can be exported to the RBD Physics schema. cmds.setKeyframe(bulletPath, at="initialVelocityX", t=0, v=5.0) cmds.setKeyframe(bulletPath, at="initialVelocityX", t=10, v=50.0) cmds.setKeyframe(bulletPath, at="initialVelocityY", t=0, v=6.0) cmds.setKeyframe(bulletPath, at="initialVelocityY", t=10, v=60.0) cmds.setKeyframe(bulletPath, at="initialVelocityZ", t=0, v=7.0) cmds.setKeyframe(bulletPath, at="initialVelocityZ", t=10, v=70.0) # Try applying the schema on a new sphere: s2T = cmds.polySphere()[0] sl.add(s2T) dagPath = sl.getDagPath(1) dagPath.extendToShape() adaptor = UsdMaya.Adaptor(dagPath.fullPathName()) physicsMass = adaptor.ApplySchemaByName("PhysicsMassAPI") self.assertEqual(physicsMass.GetName(), "PhysicsMassAPI") self.assertEqual(adaptor.GetUsdType(), Tf.Type.FindByName('UsdGeomMesh')) self.assertEqual(adaptor.GetAppliedSchemas(), ['PhysicsMassAPI']) usdDensity = physicsMass.CreateAttribute('physics:density') usdDensity.Set(33.0) # Export, but without enabling Bullet: usdFilePath = os.path.abspath('UsdExportSchemaApiTest_NoBullet.usda') cmds.mayaUSDExport(mergeTransformAndShape=True, file=usdFilePath) # Check that there are no Physics API schemas exported: stage = Usd.Stage.Open(usdFilePath) for i in (1, 2): spherePrim = stage.GetPrimAtPath( '/pSphere{0}/pSphereShape{0}'.format(i)) self.assertFalse( "PhysicsMassAPI" in spherePrim.GetAppliedSchemas()) schemasToExport = ["PhysicsMassAPI", "PhysicsRigidBodyAPI"] # Export, with Bullet: usdFilePath = os.path.abspath('UsdExportSchemaApiTest_WithBullet.usda') cmds.mayaUSDExport(mergeTransformAndShape=True, file=usdFilePath, apiSchema=schemasToExport, frameRange=(1, 10)) # Check that Physics API schemas did get exported: stage = Usd.Stage.Open(usdFilePath) values = [ ("physics:centerOfMass", (Gf.Vec3f(3, 4, 5), Gf.Vec3f(0, 0, 0))), ("physics:mass", (3.0, 1.0)), ("physics:density", (None, 33.0)), ] for i in (1, 2): spherePrim = stage.GetPrimAtPath( '/pSphere{0}/pSphereShape{0}'.format(i)) self.assertTrue("PhysicsMassAPI" in spherePrim.GetAppliedSchemas()) for n, v in values: if v[i - 1]: a = spherePrim.GetAttribute(n) self.assertEqual(a.Get(), v[i - 1]) if i == 1: # Is mass animated? a = spherePrim.GetAttribute("physics:mass") self.assertEqual(a.Get(10), 30) # This got exported even though invisible in interactive: self.assertTrue( "PhysicsRigidBodyAPI" in spherePrim.GetAppliedSchemas()) a = spherePrim.GetAttribute("physics:velocity") if i == 1: self.assertEqual(a.Get(0), (5, 6, 7)) self.assertEqual(a.Get(10), (50, 60, 70)) numberOfExportedKeys = len(a.GetTimeSamples()) # Try unapplying the schema: adaptor.UnapplySchemaByName("PhysicsMassAPI") self.assertEqual(adaptor.GetAppliedSchemas(), []) # Test import of USDPhysics without job context: cmds.file(new=True, force=True) cmds.mayaUSDImport(f=usdFilePath) sl = om.MSelectionList() # pSphereShape1 is a transform, since the bullet shape prevented merging the mesh and the # transform. The shape will be pSphereShape1Shape... sl.add("pSphereShape1") bulletPath = sl.getDagPath(0) # No bullet shape since we did not put Bullet as jobContext self.assertEqual(bulletPath.numberOfShapesDirectlyBelow(), 1) cmds.file(new=True, force=True) cmds.mayaUSDImport(f=usdFilePath, apiSchema=schemasToExport, readAnimData=True) sl = om.MSelectionList() sl.add("pSphereShape1") bulletPath = sl.getDagPath(0) # Finds bullet shape since we did put Bullet as jobContext self.assertEqual(bulletPath.numberOfShapesDirectlyBelow(), 2) # The bullet shape has animated mass and initial velocity since we read the animation. bulletPath.extendToShape(1) massDepFn = om.MFnDependencyNode(bulletPath.node()) for attrName in ("mass", "initialVelocityX", "initialVelocityY", "initialVelocityZ"): plug = om.MPlug(bulletPath.node(), massDepFn.attribute(attrName)) self.assertTrue(plug.isConnected) fcurve = oma.MFnAnimCurve(plug.source().node()) self.assertEqual(fcurve.numKeys, numberOfExportedKeys)
def _ValidateUsdLuxLight(self, lightTypeName): primPathFormat = '/RfMLightsTest/Lights/%s' lightPrimPath = primPathFormat % lightTypeName lightPrim = self._stage.GetPrimAtPath(lightPrimPath) self.assertTrue(lightPrim) testNumber = None if lightTypeName == 'CylinderLight': self.assertTrue(lightPrim.IsA(UsdLux.CylinderLight)) testNumber = 1 elif lightTypeName == 'DiskLight': self.assertTrue(lightPrim.IsA(UsdLux.DiskLight)) testNumber = 2 elif lightTypeName == 'DistantLight': self.assertTrue(lightPrim.IsA(UsdLux.DistantLight)) testNumber = 3 elif lightTypeName == 'DomeLight': self.assertTrue(lightPrim.IsA(UsdLux.DomeLight)) testNumber = 4 elif lightTypeName == 'MeshLight': self.assertTrue(lightPrim.IsA(UsdLux.GeometryLight)) testNumber = 5 elif lightTypeName == 'RectLight': self.assertTrue(lightPrim.IsA(UsdLux.RectLight)) testNumber = 6 elif lightTypeName == 'SphereLight': self.assertTrue(lightPrim.IsA(UsdLux.SphereLight)) testNumber = 7 elif lightTypeName == 'AovLight': self.assertTrue(lightPrim.GetTypeName(), "PxrAovLight") testNumber = 8 elif lightTypeName == 'EnvDayLight': self.assertTrue(lightPrim.GetTypeName(), "PxrEnvDayLight") testNumber = 9 else: raise NotImplementedError('Invalid light type %s' % lightTypeName) usdVersion = Usd.GetVersion() if usdVersion < (0, 21, 11): lightSchema = UsdLux.Light(lightPrim) else: lightSchema = UsdLux.LightAPI(lightPrim) self.assertTrue(lightSchema) if lightTypeName == 'AovLight': # PxrAovLight doesn't have any of the below attributes. return expectedIntensity = 1.0 + (testNumber * 0.1) self.assertTrue( Gf.IsClose(lightSchema.GetIntensityAttr().Get(), expectedIntensity, 1e-6)) expectedExposure = 0.1 * testNumber self.assertTrue( Gf.IsClose(lightSchema.GetExposureAttr().Get(), expectedExposure, 1e-6)) expectedDiffuse = 1.0 + (testNumber * 0.1) self.assertTrue( Gf.IsClose(lightSchema.GetDiffuseAttr().Get(), expectedDiffuse, 1e-6)) expectedSpecular = 1.0 + (testNumber * 0.1) self.assertTrue( Gf.IsClose(lightSchema.GetSpecularAttr().Get(), expectedSpecular, 1e-6)) if lightTypeName == 'EnvDayLight': # PxrEnvDayLight doesn't have any of the below attributes. return if lightTypeName == 'DomeLight': # PxrDomeLight has no normalize attribute self.assertFalse(lightSchema.GetNormalizeAttr().HasAuthoredValue()) else: expectedNormalize = True self.assertEqual(lightSchema.GetNormalizeAttr().Get(), expectedNormalize) expectedColor = Gf.Vec3f(0.1 * testNumber) self.assertTrue( Gf.IsClose(lightSchema.GetColorAttr().Get(), expectedColor, 1e-6)) expectedEnableTemperature = True self.assertEqual(lightSchema.GetEnableColorTemperatureAttr().Get(), expectedEnableTemperature) expectedTemperature = 6500.0 + testNumber self.assertTrue( Gf.IsClose(lightSchema.GetColorTemperatureAttr().Get(), expectedTemperature, 1e-6))
def createAppliedSchemasSection(self): # USD version 0.21.2 is required because of # Usd.SchemaRegistry().GetPropertyNamespacePrefix() if Usd.GetVersion() < (0, 21, 2): return showAppliedSchemasSection = False # loop on all applied schemas and store all those # schema into a dictionary with the attributes. # Storing the schema into a dictionary allow us to # group all instances of a MultipleApply schema together # so we can later display them into the same UI section. # # By example, if UsdCollectionAPI is applied twice, UsdPrim.GetAppliedSchemas() # will return ["CollectionAPI:instance1","CollectionAPI:instance2"] but we want to group # both instance inside a "CollectionAPI" section. # schemaAttrsDict = {} appliedSchemas = self.prim.GetAppliedSchemas() for schema in appliedSchemas: if Usd.GetVersion() > (0, 21, 5): typeAndInstance = Usd.SchemaRegistry().GetTypeNameAndInstance( schema) else: typeAndInstance = Usd.SchemaRegistry().GetTypeAndInstance( schema) typeName = typeAndInstance[0] schemaType = Usd.SchemaRegistry().GetTypeFromName(typeName) if schemaType.pythonClass: isMultipleApplyAPISchema = Usd.SchemaRegistry( ).IsMultipleApplyAPISchema(typeName) if isMultipleApplyAPISchema: # get the attributes names. They will not include the namespace and instance name. instanceName = typeAndInstance[1] attrList = schemaType.pythonClass.GetSchemaAttributeNames( False, instanceName) # build the real attr name # By example, collection:lightLink:includeRoot namespace = Usd.SchemaRegistry( ).GetPropertyNamespacePrefix(typeName) prefix = namespace + ":" + instanceName + ":" attrList = [prefix + i for i in attrList] if typeName in schemaAttrsDict: schemaAttrsDict[typeName] += attrList else: schemaAttrsDict[typeName] = attrList else: attrList = schemaType.pythonClass.GetSchemaAttributeNames( False) schemaAttrsDict[typeName] = attrList # The "Applied Schemas" will be only visible if at least # one applied Schemas has attribute. if not showAppliedSchemasSection: for attr in attrList: if self.attrS.hasAttribute(attr): showAppliedSchemasSection = True break # Create the "Applied Schemas" section # with all the applied schemas if showAppliedSchemasSection: with ufeAeTemplate.Layout(self, 'Applied Schemas', collapse=True): for typeName, attrs in schemaAttrsDict.items(): typeName = self.sectionNameFromSchema(typeName) self.createSection(typeName, attrs, False)
class testUsdExportImportRoundtripPreviewSurface(unittest.TestCase): @classmethod def setUpClass(cls): cls.inputPath = fixturesUtils.setUpClass(__file__) test_dir = os.path.join(cls.inputPath, "UsdExportImportRoundtripPreviewSurface") if not os.path.isdir(test_dir): os.mkdir(test_dir) cmds.workspace(test_dir, o=True) @classmethod def tearDownClass(cls): standalone.uninitialize() def testUsdPreviewSurfaceRoundtripSpecular(self): self.__testUsdPreviewSurfaceRoundtrip(metallic=False) def testUsdPreviewSurfaceRoundtripMetallic(self): self.__testUsdPreviewSurfaceRoundtrip(metallic=True) @unittest.skipUnless("mayaUtils" in globals() and mayaUtils.previewReleaseVersion() >= 126 and Usd.GetVersion() > (0, 21, 2), 'Requires MaterialX support.') def testUsdPreviewSurfaceRoundtripMaterialX(self): self.__testUsdPreviewSurfaceRoundtrip(metallic=True, convertTo="MaterialX") def __testUsdPreviewSurfaceRoundtrip(self, metallic=True, convertTo="UsdPreviewSurface"): """ Tests that a usdPreviewSurface exports and imports correctly. """ mark = Tf.Error.Mark() mark.SetMark() self.assertTrue(mark.IsClean()) mayaUsdPluginName = "mayaUsdPlugin" if not cmds.pluginInfo(mayaUsdPluginName, query=True, loaded=True): cmds.loadPlugin(mayaUsdPluginName) cmds.file(f=True, new=True) sphere_xform = cmds.polySphere()[0] material_node = cmds.shadingNode("usdPreviewSurface", asShader=True, name="usdPreviewSurface42") material_sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name=material_node+"SG") cmds.connectAttr(material_node+".outColor", material_sg+".surfaceShader", force=True) cmds.sets(sphere_xform, e=True, forceElement=material_sg) cmds.setAttr(material_node + ".ior", 2) cmds.setAttr(material_node + ".roughness", 0.25) cmds.setAttr(material_node + ".specularColor", 0.125, 0.25, 0.75, type="double3") cmds.setAttr(material_node + ".useSpecularWorkflow", not metallic) cmds.setAttr(material_node + ".opacityThreshold", 0.5) file_node = cmds.shadingNode("file", asTexture=True, isColorManaged=True) uv_node = cmds.shadingNode("place2dTexture", asUtility=True) connectUVNode(uv_node, file_node) cmds.connectAttr(file_node + ".outColor", material_node + ".diffuseColor", f=True) txfile = os.path.join("..", "textures", "Brazilian_rosewood_pxr128.png") cmds.setAttr(file_node+".fileTextureName", txfile, type="string") cmds.setAttr(file_node+".colorSpace", "ACEScg", type="string") cmds.setAttr(file_node + ".defaultColor", 0.5, 0.25, 0.125, type="double3") default_ext_setting = cmds.file(q=True, defaultExtensions=True) cmds.file(defaultExtensions=False) cmds.setAttr(uv_node+".wrapU", 0) original_path = cmds.getAttr(file_node+".fileTextureName") # Export to USD: file_suffix = "_{}_{}".format(convertTo, 'Metallic' if metallic else 'Specular') usd_path = os.path.abspath('UsdPreviewSurfaceRoundtripTest{}.usda'.format(file_suffix)) export_options = [ "shadingMode=useRegistry", "convertMaterialsTo=[{}]".format(convertTo), "mergeTransformAndShape=1" ] cmds.file(usd_path, force=True, options=";".join(export_options), typ="USD Export", pr=True, ea=True) cmds.file(defaultExtensions=default_ext_setting) cmds.file(newFile=True, force=True) # Import back: import_options = ("shadingMode=[[useRegistry,{}]]".format(convertTo), "preferredMaterial=none", "primPath=/") cmds.file(usd_path, i=True, type="USD Import", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="Test", pr=True, importTimeRange="combine", options=";".join(import_options)) # Check the new sphere is in the new shading group: self.assertTrue(cmds.sets( "pSphere1Shape", isMember=material_sg)) # Check that we have no spurious "Looks" transform expectedTr = set(['front', 'persp', 'side', 'top', 'pSphere1']) allTr = set(cmds.ls(tr=True)) self.assertEqual(allTr, expectedTr) # Check connections: self.assertEqual( cmds.connectionInfo(material_node+".outColor", dfs=True), [material_sg+".surfaceShader"]) self.assertEqual( cmds.connectionInfo(material_node+".diffuseColor", sfd=True), file_node+".outColor") self.assertEqual( cmds.connectionInfo(file_node+".wrapU", sfd=True), "place2dTexture.wrapU") # Check values: self.assertAlmostEqual(cmds.getAttr(material_node+".ior"), 2) self.assertAlmostEqual(cmds.getAttr(material_node+".roughness"), 0.25) self.assertAlmostEqual(cmds.getAttr(material_node+".opacityThreshold"), 0.5) self.assertEqual(cmds.getAttr(material_node+".specularColor"), [(0.125, 0.25, 0.75)]) self.assertEqual(cmds.getAttr(material_node+".useSpecularWorkflow"), int(not metallic)) self.assertEqual(cmds.getAttr(file_node+".defaultColor"), [(0.5, 0.25, 0.125)]) self.assertEqual(cmds.getAttr(file_node+".colorSpace"), "ACEScg") self.assertEqual(cmds.getAttr(file_node+".colorSpace"), "ACEScg") imported_path = cmds.getAttr(file_node+".fileTextureName") # imported path will be absolute: self.assertFalse(imported_path.startswith("..")) self.assertEqual(os.path.normpath(imported_path.lower()), os.path.normpath(original_path.lower())) self.assertEqual(cmds.getAttr("place2dTexture.wrapU"), 0) self.assertEqual(cmds.getAttr("place2dTexture.wrapV"), 1) # Make sure paths are relative in the USD file. Joining the directory # that the USD file lives in with the texture path should point us at # a file that exists. file_template = "/{}/Looks/{}/{}" if convertTo == "MaterialX": file_template = "/{0}/Looks/{1}/MayaNG_{1}/{2}" stage = Usd.Stage.Open(usd_path) texture_prim = stage.GetPrimAtPath( file_template.format(sphere_xform, material_sg, file_node)) rel_texture_path = texture_prim.GetAttribute('inputs:file').Get().path usd_dir = os.path.dirname(usd_path) full_texture_path = os.path.join(usd_dir, rel_texture_path) self.assertTrue(os.path.isfile(full_texture_path)) self.assertTrue(mark.IsClean()) def testShadingRoundtrip(self): """ Test that shading group and surface node names will survive a roundtrip """ mark = Tf.Error.Mark() mark.SetMark() self.assertTrue(mark.IsClean()) mayaUsdPluginName = "mayaUsdPlugin" if not cmds.pluginInfo(mayaUsdPluginName, query=True, loaded=True): cmds.loadPlugin(mayaUsdPluginName) testPatterns = [ # Each test has 5 elements: # - Shader type # - Initial shader name # - Initial shading group name # - Roundtrip shader name # - Roundtrip shading group name # Fully modified names will survive a roundtrip: ("lambert", "bob", "bobSG", "bob", "bobSG"), # Modified sg name survive (and we do not touch surface name) ("blinn", "blinn42", "blueSG", "blinn42", "blueSG"), # Default surface names will survive even if mismatched: ("phong", "phong12", "blinn27SG", "phong12", "blinn27SG"), # WARNING: Meaninful surface names win over boring shading group # names, so this combination does not roundtrip. The final shading # group name will be modified to be consistent with the surface # shader name: ("blinn", "myGold", "blinn12SG", "myGold", "myGoldSG"), ("usdPreviewSurface", "jersey12", "blinn27SG", "jersey12", "jersey12SG"), # This will make the UsdMaterial and UsdGeomSubset names more # meaningful. ] for sh_type, init_surf, init_sg, final_surf, final_sg in testPatterns: cmds.file(f=True, new=True) sphere_xform = cmds.polySphere()[0] material_node = cmds.shadingNode(sh_type, asShader=True, name=init_surf) material_sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name=init_sg) cmds.connectAttr(material_node+".outColor", material_sg+".surfaceShader", force=True) cmds.sets(sphere_xform, e=True, forceElement=material_sg) default_ext_setting = cmds.file(q=True, defaultExtensions=True) cmds.file(defaultExtensions=False) # Export to USD: usd_path = os.path.abspath('%sRoundtripTest.usda' % init_surf) cmds.file(usd_path, force=True, options="shadingMode=useRegistry;mergeTransformAndShape=1", typ="USD Export", pr=True, ea=True) cmds.file(defaultExtensions=default_ext_setting) cmds.file(newFile=True, force=True) # Import back: import_options = ("shadingMode=[[useRegistry,UsdPreviewSurface]]", "preferredMaterial=none", "primPath=/") cmds.file(usd_path, i=True, type="USD Import", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="Test", pr=True, importTimeRange="combine", options=";".join(import_options)) # Check shading group name: self.assertTrue(cmds.sets("pSphere1Shape", isMember=final_sg)) # Check surface name: self.assertEqual( cmds.connectionInfo(final_surf+".outColor", dfs=True), [final_sg+".surfaceShader"]) self.assertTrue(mark.IsClean()) def testDisplayColorLossyRoundtrip(self): """ Test that shading group names created for display color import are in sync with their surface shaders. """ mark = Tf.Error.Mark() mark.SetMark() self.assertTrue(mark.IsClean()) mayaUsdPluginName = "mayaUsdPlugin" if not cmds.pluginInfo(mayaUsdPluginName, query=True, loaded=True): cmds.loadPlugin(mayaUsdPluginName) for i in range(1,4): sphere_xform = cmds.polySphere()[0] init_surf = "test%i" % i init_sg = init_surf + "SG" material_node = cmds.shadingNode("lambert", asShader=True, name=init_surf) material_sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name=init_sg) cmds.connectAttr(material_node+".outColor", material_sg+".surfaceShader", force=True) cmds.sets(sphere_xform, e=True, forceElement=material_sg) # Export to USD: usd_path = os.path.abspath('DisplayColorRoundtripTest.usda') cmds.usdExport(mergeTransformAndShape=True, file=usd_path, shadingMode='none', exportDisplayColor=True) for preferred in ("blinn", "phong"): cmds.file(newFile=True, force=True) import_options = ("shadingMode=[[displayColor,default]]", "preferredMaterial=%s" % preferred, "primPath=/") cmds.file(usd_path, i=True, type="USD Import", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="Test", pr=True, importTimeRange="combine", options=";".join(import_options)) for i in ("", "1", "2"): # We expect blinn, blinn1, blinn2 final_surf = "%s%s" % (preferred, i) # We expect blinnSG, blinn1SG, blinn2SG final_sg = final_surf + "SG" # Check surface name: self.assertEqual( cmds.connectionInfo(final_surf+".outColor", dfs=True), [final_sg+".surfaceShader"]) self.assertTrue(mark.IsClean()) def testUVReaderMerging(self): """ Test that we produce a minimal number of UV readers """ cmds.file(f=True, new=True) sphere_xform = cmds.polySphere()[0] material_node = cmds.shadingNode("usdPreviewSurface", asShader=True, name="ss01") material_sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name="ss01SG") cmds.connectAttr(material_node+".outColor", material_sg+".surfaceShader", force=True) cmds.sets(sphere_xform, e=True, forceElement=material_sg) # One file with UVs connected to diffuse: file_node = cmds.shadingNode("file", asTexture=True, isColorManaged=True) uv_node = cmds.shadingNode("place2dTexture", asUtility=True) cmds.setAttr(uv_node + ".offsetU", 0.125) cmds.setAttr(uv_node + ".offsetV", 0.5) connectUVNode(uv_node, file_node) cmds.connectAttr(file_node + ".outColor", material_node + ".diffuseColor", f=True) # Another file, same UVs, connected to emissiveColor file_node = cmds.shadingNode("file", asTexture=True, isColorManaged=True) connectUVNode(uv_node, file_node) cmds.connectAttr(file_node + ".outColor", material_node + ".emissiveColor", f=True) # Another file, no UVs, connected to metallic file_node = cmds.shadingNode("file", asTexture=True, isColorManaged=True) cmds.connectAttr(file_node + ".outColorR", material_node + ".metallic", f=True) # Another file, no UVs, connected to roughness file_node = cmds.shadingNode("file", asTexture=True, isColorManaged=True) cmds.connectAttr(file_node + ".outColorR", material_node + ".roughness", f=True) cmds.setAttr(file_node + ".offsetU", 0.25) cmds.setAttr(file_node + ".offsetV", 0.75) # Export to USD: usd_path = os.path.abspath('MinimalUVReader.usda') cmds.usdExport(mergeTransformAndShape=True, file=usd_path, shadingMode='useRegistry', exportDisplayColor=True) # We expect 2 primvar readers, and 2 st transforms: stage = Usd.Stage.Open(usd_path) mat_path = "/pSphere1/Looks/ss01SG/" # Here are the expected connections in the produced USD file: connections = [ # Source node, input, destination node: ("ss01", "diffuseColor", "file1"), ("file1", "st", "place2dTexture1_UsdTransform2d"), ("place2dTexture1_UsdTransform2d", "in", "place2dTexture1"), ("ss01", "emissiveColor", "file2"), ("file2", "st", "place2dTexture1_UsdTransform2d"), # re-used # Note that the transform name is derived from place2DTexture name. ("ss01", "metallic", "file3"), ("file3", "st", "shared_TexCoordReader"), # no UV in Maya. ("ss01", "roughness", "file4"), ("file4", "st", "file4_UsdTransform2d"), # xform on file node ("file4_UsdTransform2d", "in", "shared_TexCoordReader") # Note that the transform name is derived from file node name. ] for src_name, input_name, dst_name in connections: src_prim = stage.GetPrimAtPath(mat_path + src_name) self.assertTrue(src_prim) src_shade = UsdShade.Shader(src_prim) self.assertTrue(src_shade) src_input = src_shade.GetInput(input_name) self.assertTrue(src_input.HasConnectedSource()) (connect_api, out_name, _) = src_input.GetConnectedSource() self.assertEqual(connect_api.GetPath(), mat_path + dst_name) @unittest.skipUnless("mayaUtils" in globals() and mayaUtils.mayaMajorVersion() >= 2020, 'Requires standardSurface node which appeared in 2020.') def testOpacityRoundtrip(self): """ Test that opacity roundtrips as expected. """ filePath = os.path.join(self.inputPath, "UsdExportImportRoundtripPreviewSurfaceTest", "OpacityTests.ma") cmds.file(filePath, force=True, open=True) usd_path = os.path.abspath('OpacityRoundtripTest.usda') cmds.usdExport(mergeTransformAndShape=True, file=usd_path, shadingMode='useRegistry') stage = Usd.Stage.Open(usd_path) # We have 7 materials that are named: # /pPlane6/Looks/standardSurface7SG/standardSurface7 surf_base = "/pPlane{0}/Looks/standardSurface{1}SG/standardSurface{1}" # results for opacity are mostly connections to: # /pPlane6/Looks/standardSurface7SG/file6.outputs:a cnx_base = "/pPlane{0}/Looks/standardSurface{1}SG/file{0}" # so we only need to expect a channel name: expected = ["r", "a", "r", "a", "g", "a", 0.4453652] for i, val in enumerate(expected): surf_path = surf_base.format(i+1, i+2) surf_prim = stage.GetPrimAtPath(surf_path) self.assertTrue(surf_prim) surf_shade = UsdShade.Shader(surf_prim) self.assertTrue(surf_shade) # useSpecularWorkflow is not exported anymore: use_specular_workflow = surf_shade.GetInput("useSpecularWorkflow") self.assertFalse(use_specular_workflow) opacity = surf_shade.GetInput("opacity") self.assertTrue(opacity) if (isinstance(val, float)): self.assertAlmostEqual(opacity.Get(), val) else: (connect_api, out_name, _) = opacity.GetConnectedSource() self.assertEqual(out_name, val) cnx_string = cnx_base.format(i+1, i+2) self.assertEqual(connect_api.GetPath(), cnx_string) cmds.file(f=True, new=True) # Re-import for a full roundtrip: # Import back: import_options = ("shadingMode=[[useRegistry,UsdPreviewSurface]]", "preferredMaterial=standardSurface", "primPath=/") cmds.file(usd_path, i=True, type="USD Import", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, namespace="Test", pr=True, importTimeRange="combine", options=";".join(import_options)) # The roundtrip is not perfect. We use explicit connections everywhere # on import: # # We expect the following connections: # {'standardSurface2.opacityR': 'file1.outColorR', # 'standardSurface2.opacityG': 'file1.outColorR', # 'standardSurface2.opacityB': 'file1.outColorR'} # port_names = {"r": "outColorR", "g": "outColorG", "b": "outColorB", "a": "outAlpha"} opacity_names = ["opacityR", "opacityG", "opacityB"] for i, val in enumerate(expected): if (isinstance(val, float)): for v in cmds.getAttr("standardSurface8.opacity")[0]: self.assertAlmostEqual(v, val) else: cnx = cmds.listConnections("standardSurface{}".format(i+2), d=False, c=True, p=True) self.assertEqual(len(cnx), 6) for j in range(int(len(cnx)/2)): k = cnx[2*j].split(".") v = cnx[2*j+1].split(".") self.assertEqual(len(k), 2) self.assertEqual(len(v), 2) self.assertTrue(k[1] in opacity_names) self.assertEqual(v[0], "file{}".format(i+1)) self.assertEqual(v[1], port_names[val]) cmds.file(f=True, new=True)
def testExportAsClip(self): """ Test that a maya scene exports to usd the same way if it is exported all at once, or in 5 frame clips and then stitched back together. """ # generate clip files and validate num samples on points attribute clipFiles = [] # first 5 frames have no animation usdFile = os.path.abspath('UsdExportAsClip_cube.001.usda') clipFiles.append(usdFile) cmds.usdExport(mergeTransformAndShape=True, file=usdFile, frameRange=(1, 5), sss=False) stage = Usd.Stage.Open(usdFile) self._ValidateNumSamples(stage, '/world/pCube1', 'points', 1) # next 5 frames have no animation usdFile = os.path.abspath('UsdExportAsClip_cube.005.usda') clipFiles.append(usdFile) cmds.usdExport(mergeTransformAndShape=True, file=usdFile, frameRange=(5, 10), sss=False) stage = Usd.Stage.Open(usdFile) self._ValidateNumSamples(stage, '/world/pCube1', 'points', 1) # next 5 frames have deformation animation usdFile = os.path.abspath('UsdExportAsClip_cube.010.usda') clipFiles.append(usdFile) frames = (10, 15) cmds.usdExport(mergeTransformAndShape=True, file=usdFile, frameRange=frames, sss=False) stage = Usd.Stage.Open(usdFile) self._ValidateNumSamples(stage, '/world/pCube1', 'points', frames[1] + 1 - frames[0]) # next 5 frames have no animation usdFile = os.path.abspath('UsdExportAsClip_cube.015.usda') clipFiles.append(usdFile) cmds.usdExport(mergeTransformAndShape=True, file=usdFile, frameRange=(15, 20), sss=False) stage = Usd.Stage.Open(usdFile) self._ValidateNumSamples(stage, '/world/pCube1', 'points', 1) stitchedPath = os.path.abspath('result.usda') stitchedLayer = Sdf.Layer.CreateNew(stitchedPath) # Clip stitching behavior changed significantly between core USD 20.05 # and 20.08. Beginning with 20.08, we need to pass an additional option # to ensure that authored time samples are held across gaps in value # clips. if Usd.GetVersion() > (0, 20, 5): self.assertTrue( UsdUtils.StitchClips(stitchedLayer, clipFiles, '/world', startFrame=1, endFrame=20, interpolateMissingClipValues=True, clipSet='default')) else: self.assertTrue( UsdUtils.StitchClips(stitchedLayer, clipFiles, '/world', startFrame=1, endFrame=20, clipSet='default')) # export a non clip version for comparison canonicalUsdFile = os.path.abspath('canonical.usda') cmds.usdExport(mergeTransformAndShape=True, file=canonicalUsdFile, frameRange=(1, 20), sss=False) print('comparing: \nnormal: {}\nstitched: {}'.format( canonicalUsdFile, stitchedPath)) canonicalStage = Usd.Stage.Open(canonicalUsdFile) clipsStage = Usd.Stage.Open(stitchedPath) # visible self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube1', 'visibility', (0, 21)) # animated visibility self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube2', 'visibility', (0, 21)) # hidden, non animated: self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube4', 'visibility', (0, 21)) # constant points: self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube2', 'points', (0, 21)) # blend shape driven animated points: self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube3', 'points', (0, 21)) # animated points: self._ValidateSamples(canonicalStage, clipsStage, '/world/pCube1', 'points', (0, 21))