示例#1
0
    def testReexport(self):
        cmds.mayaUSDImport(file=self._usdFile, primPath='/')

        exportedUsdFile = os.path.abspath('ExoticTypeNames.reexported.usda')
        cmds.mayaUSDExport(file=exportedUsdFile)

        stage = Usd.Stage.Open(exportedUsdFile)
        self.assertTrue(stage)

        self.assertTrue(stage.GetPrimAtPath('/A'))
        self.assertEqual(stage.GetPrimAtPath('/A').GetTypeName(), 'Xform')
        self.assertTrue(stage.GetPrimAtPath('/A/A_1'))
        self.assertEqual(stage.GetPrimAtPath('/A/A_1').GetTypeName(), '')
        self.assertTrue(stage.GetPrimAtPath('/A/A_1/A_1_I'))
        self.assertEqual(stage.GetPrimAtPath('/A/A_1/A_1_I').GetTypeName(),
                '')
        self.assertTrue(stage.GetPrimAtPath('/A/A_1/A_1_II'))
        self.assertEqual(stage.GetPrimAtPath('/A/A_1/A_1_II').GetTypeName(),
                '') # Originally Cube, but not on re-export!
        self.assertTrue(stage.GetPrimAtPath('/A/A_1/A_1_III'))
        self.assertEqual(stage.GetPrimAtPath('/A/A_1/A_1_III').GetTypeName(),
                'Scope')
        self.assertTrue(stage.GetPrimAtPath('/A/A_2'))
        self.assertEqual(stage.GetPrimAtPath('/A/A_2').GetTypeName(), 'Scope')
        self.assertTrue(stage.GetPrimAtPath('/B'))
        self.assertEqual(stage.GetPrimAtPath('/B').GetTypeName(), '')
        self.assertTrue(stage.GetPrimAtPath('/B/B_1'))
        self.assertEqual(stage.GetPrimAtPath('/B/B_1').GetTypeName(), 'Xform')
    def testImportSimpleUnoptimized(self):
        """
        Test importing the following non-optimized USD simple instancing
        scenario:

        pCube1 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
        pCube2 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
        pCube3 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
        MayaExportedInstanceSources
            pCube1_pCubeShape1 [Scope]
                pCubeShape1 [Mesh]

        The goal would be for Maya to skip all [Scope] items and end up with:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
        pCube2 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
        pCube3 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)

        But that will happen in a second phase. We only make sure this imports
        without coding errors here.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "SimpleScenario_unoptimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # pCube1 is not an instance:
            ('pCube1',),
            # Bad: We have an extra transform added here for the USD scope and
            # no sharing of first pCubeShape1 transform.
            ('pCube1|pCubeShape1',),
            ('pCube2|pCubeShape1',),
            ('pCube3|pCubeShape1',),
            # Correctly shared via instancing, but with 2 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1',
             'pCube2|pCubeShape1|pCubeShape1',
             'pCube3|pCubeShape1|pCubeShape1'),

            ('pCube1|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube2|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube3|pCubeShape1|pCubeShape1|pCubeShape1Shape'),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
    def setUpClass(cls):
        inputPath = fixturesUtils.readOnlySetUpClass(__file__)

        usdFilePath = os.path.join(inputPath, 'UsdImportRfMShadersTest',
            'MarbleCube.usda')

        cls._stage = Usd.Stage.Open(usdFilePath)

        cmds.file(new=True, force=True)

        cmds.mayaUSDImport(file=usdFilePath, shadingMode=[['useRegistry', 'rendermanForMaya'], ])
    def test_framerate_imports(self):
        """Test that the USD's timesamples are appropriately
        converted to match the Mayas uiUnits"""

        # Only check a few of these since the logic should
        # hold up the same no matter what, but
        fps_map = {"ntsc": 30, "film": 24, "ntscf": 60}

        base_rate = self.stage.GetTimeCodesPerSecond()
        start = self.stage.GetStartTimeCode()
        end = self.stage.GetEndTimeCode()

        nodes = {
            'Root|joint1': self.getKeyframes("/Root/Skin"),
            'Root|Mesh': self.getKeyframes("/Root/Mesh"),
            "blendShape1": self.getKeyframes("/Root/Skin"),  # Mesh Anim
            "Cam": self.getKeyframes("/Root/Cam"),
            # For some reason the clip planes don't identify keys on the Cam Shape directly
            "CamShape_nearClipPlane": self.getKeyframes("/Root/Cam"),
            "blendShape2": self.getKeyframes("/Root/Nurbs")  # Patch anim
        }

        # TODO: Add NurbsCurves to the nodes list when the importer supports it

        for name, fps in fps_map.items():
            cmds.file(new=True, force=True)
            cmds.currentUnit(time=name)
            cmds.playbackOptions(minTime=10)
            cmds.playbackOptions(maxTime=20)

            time_scale = (fps / base_rate)

            cmds.mayaUSDImport(f=self.usd_file, readAnimData=True)

            # First make sure the playback slider is set correctly
            self.assertAlmostEquals(
                start * time_scale,
                cmds.playbackOptions(query=True, minTime=True))
            self.assertAlmostEquals(
                end * time_scale, cmds.playbackOptions(query=True,
                                                       maxTime=True))

            for node, frames in nodes.items():
                mframes = sorted(set(cmds.keyframe(node, query=True)))

                numMFrames = len(mframes)
                numFrames = len(frames)
                self.assertTrue(
                    numFrames == numMFrames,
                    "A mismatched number of frames was found {}:{}".format(
                        numFrames, numMFrames))

                for idx, frame in enumerate(frames):
                    self.assertAlmostEquals(frame * time_scale, mframes[idx])
示例#5
0
    def testNoDisplayColors(self):
        """
        Do not get default down to display colors:
        """
        modes = [["useRegistry", "maya"], ["useRegistry", "UsdPreviewSurface"]]
        cmds.mayaUSDImport(file=self.usd_path,
                           shadingMode=modes,
                           preferredMaterial="none",
                           primPath="/")

        expected = [["pCube1Shape", None], ["pCube2Shape", "standardSurface"],
                    ["pCube3Shape", "usdPreviewSurface"]]
        self.checkMaterials(expected)
示例#6
0
    def testNoImport(self):
        """
        Do not import any materials.
        """

        modes = [
            ["none", "default"],
        ]
        cmds.mayaUSDImport(file=self.usd_path, shadingMode=modes, primPath="/")

        expected = [["pCube1Shape", None], ["pCube2Shape", None],
                    ["pCube3Shape", None]]

        self.checkMaterials(expected)
    def testImportSimpleOptimized(self):
        """
        Test importing the following optimized USD simple instancing scenario:

        pCube1 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube2 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube3 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        MayaExportedInstanceSources
            pCube1_instanceParent [Xform]
                pCubeShape1 [Mesh]

        The goal would be for Maya to end up with this:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
        pCube2 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
        pCube3 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)

        This would require finding out that pCube1_instanceParent was not
        merged with pCubeShape1 and do not require adding an extra unmerging
        transform when reading the Gprim.

        But that will happen in a second phase. We only make sure this imports
        without coding errors here.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "SimpleScenario_optimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # pCube1 is not an instance:
            ('pCube1',),
            # Correctly shared via instancing, but with 1 extra transforms:
            ('pCube1|pCubeShape1',
             'pCube2|pCubeShape1',
             'pCube3|pCubeShape1'),

            ('pCube1|pCubeShape1|pCubeShape1Shape',
             'pCube2|pCubeShape1|pCubeShape1Shape',
             'pCube3|pCubeShape1|pCubeShape1Shape'),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
示例#8
0
    def testUsdImport(self):
        """
        This test executes import from an anonymous layer as per GitHub Discussion
        https://github.com/Autodesk/maya-usd/discussions/1069
        """

        layer = Sdf.Layer.CreateAnonymous()
        stage = Usd.Stage.Open(layer)
        stage.DefinePrim('/Foo', 'Xform')

        cmds.mayaUSDImport(f=layer.identifier, primPath='/')

        expectedMayaNodesSet = set(['|Foo'])
        mayaNodesSet = set(cmds.ls('|Foo*', long=True))
        self.assertEqual(expectedMayaNodesSet, mayaNodesSet)
    def testImportExportInstancesTest(self):
        """
        Test importing the USD currently generated by testExportInstances.

        The goal would be for Maya to end up with the same node hierarchy as
        the original scene:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
            pCube2 [transform] (instance "source")
                pCubeShape2 [mesh] (instance "source")
            pCube3 [transform] (instance "source")
                pCubeShape2 (instance pCube1|pCube2|pCubeShape2)
        pCube4 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)

        There is some work to do to achieve that.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "UsdExportInstancesTest_original.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # Correctly shared via instancing, but with 2 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube4|pCubeShape1|pCubeShape1|pCubeShape1Shape'),

            ('pCube1|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube1|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape'),
            # Incorrect: Would expect all pCube2 to be shared:
            ('pCube1|pCube2',),

            ('pCube4|pCube2',),
            # Incorrect: Would expect all pCube3 to be shared:
            ('pCube1|pCube3',),

            ('pCube4|pCube3',),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
示例#10
0
    def testUSDZImport(self):
        mark = Tf.Error.Mark()
        mark.SetMark()
        self.assertTrue(mark.IsClean())

        om.MFileIO.newFile(True)
        write_dir_path = self.temp_dir
        cmds.mayaUSDImport(f=self.usdz_path,
                           importUSDZTextures=True,
                           importUSDZTexturesFilePath=write_dir_path)
        self.assertTrue(
            os.path.isfile(os.path.join(write_dir_path, "clouds_128_128.png")))
        self.assertTrue(
            os.path.isfile(os.path.join(write_dir_path, "red_128_128.png")))

        self.assertTrue(mark.IsClean())
示例#11
0
    def testImportCommand(self):
        """
        Tests a custom exporter for a conversion that exists in an unloaded
        plugin using the mayaUSDImport command.
        """
        modes = [["useRegistry", "maya"], ["useRegistry", "UsdPreviewSurface"],
                 ["displayColor", "default"]]
        cmds.mayaUSDImport(file=self.usd_path,
                           shadingMode=modes,
                           preferredMaterial="none",
                           primPath="/")

        expected = [["pCube1Shape", "lambert"],
                    ["pCube2Shape", "standardSurface"],
                    ["pCube3Shape", "usdPreviewSurface"]]
        self.checkMaterials(expected)
示例#12
0
    def testImport(self):
        cmds.mayaUSDImport(file=self._usdFile, primPath='/')
        dagObjects = cmds.ls(long=True, dag=True)

        self.assertIn('|A', dagObjects)
        self.assertEqual(cmds.nodeType('|A'), 'transform')
        self.assertFalse(cmds.getAttr('|A.tx', lock=True))

        self.assertIn('|A|A_1', dagObjects)
        self.assertEqual(cmds.nodeType('|A|A_1'), 'transform')
        self.assertEqual(cmds.getAttr('|A|A_1.USD_typeName'), '')
        self.assertTrue(cmds.getAttr('|A|A_1.tx', lock=True))

        self.assertIn('|A|A_1|A_1_I', dagObjects)
        self.assertEqual(cmds.nodeType('|A|A_1|A_1_I'), 'transform')
        self.assertEqual(cmds.getAttr('|A|A_1|A_1_I.USD_typeName'), '')
        self.assertTrue(cmds.getAttr('|A|A_1|A_1_I.tx', lock=True))

        # This one is special: unknown types get a Maya "note" on import for
        # aid debugging import problems. (Note that we don't have a Cube
        # importer.)
        self.assertIn('|A|A_1|A_1_II', dagObjects)
        self.assertEqual(cmds.nodeType('|A|A_1|A_1_II'), 'transform')
        self.assertEqual(cmds.getAttr('|A|A_1|A_1_II.USD_typeName'), '')
        self.assertIn('Cube', cmds.getAttr('|A|A_1|A_1_II.notes'))
        self.assertTrue(cmds.getAttr('|A|A_1|A_1_II.tx', lock=True))

        self.assertIn('|A|A_1|A_1_III', dagObjects)
        self.assertEqual(cmds.nodeType('|A|A_1|A_1_III'), 'transform')
        self.assertEqual(cmds.getAttr('|A|A_1|A_1_III.USD_typeName'), 'Scope')
        self.assertTrue(cmds.getAttr('|A|A_1|A_1_III.tx', lock=True))

        self.assertIn('|A|A_2', dagObjects)
        self.assertEqual(cmds.nodeType('|A|A_2'), 'transform')
        self.assertEqual(cmds.getAttr('|A|A_2.USD_typeName'), 'Scope')
        self.assertTrue(cmds.getAttr('|A|A_2.tx', lock=True))

        self.assertIn('|B', dagObjects)
        self.assertEqual(cmds.nodeType('|B'), 'transform')
        self.assertEqual(cmds.getAttr('|B.USD_typeName'), '')
        self.assertTrue(cmds.getAttr('|B.tx', lock=True))

        self.assertIn('|B|B_1', dagObjects)
        self.assertEqual(cmds.nodeType('|B|B_1'), 'transform')
        self.assertFalse(cmds.getAttr('|B|B_1.tx', lock=True))
示例#13
0
    def testUSDZImportIntoProject(self):
        mark = Tf.Error.Mark()
        mark.SetMark()
        self.assertTrue(mark.IsClean())

        project_path = os.path.normpath(cmds.workspace(q=True, rd=True))
        image_path = os.path.join(project_path,
                                  cmds.workspace(fre="sourceImages"))
        # Purposefully erase the sourceimage directory:
        if os.path.isdir(image_path):
            shutil.rmtree(image_path)

        # It will automatically get recreated:
        om.MFileIO.newFile(True)
        cmds.mayaUSDImport(f=self.usdz_path, importUSDZTextures=True)
        self.assertTrue(
            os.path.isfile(os.path.join(image_path, "clouds_128_128.png")))
        self.assertTrue(
            os.path.isfile(os.path.join(image_path, "red_128_128.png")))

        self.assertTrue(mark.IsClean())
示例#14
0
 def testSimpleImportChaser(self):
     mayaUsdLib.ImportChaser.Register(importChaserTest, "info")
     rootPaths = cmds.mayaUSDImport(v=True, f=self.stagePath, chaser=['info'])
     self.assertEqual(len(rootPaths), 1)
     sl = OpenMaya.MSelectionList()
     sl.add(rootPaths[0])
     root = sl.getDependNode(0)
     fnNode = OpenMaya.MFnDependencyNode(root)
     self.assertTrue(fnNode.hasAttribute("customData"))
     plgCustomData = fnNode.findPlug("customData", True)
     customDataStr = plgCustomData.asString()
     self.assertEqual(customDataStr, "Custom layer data: customKeyAcustomValueA\ncustomKeyBcustomValueB\n")
示例#15
0
 def testImportChaser(self):
     cmds.loadPlugin('usdTestInfoImportChaser')  # NOTE: (yliangsiew) We load this plugin since the chaser is compiled as part of it.
     rootPaths = cmds.mayaUSDImport(v=True, f=self.stagePath, chaser=['info'])
     self.assertEqual(len(rootPaths), 1)
     sl = om.MSelectionList()
     sl.add(rootPaths[0])
     root = om.MObject()
     sl.getDependNode(0, root)
     fnNode = om.MFnDependencyNode(root)
     self.assertTrue(fnNode.hasAttribute("customData"))
     plgCustomData = fnNode.findPlug("customData")
     customDataStr = plgCustomData.asString()
     self.assertEqual(customDataStr, "Custom layer data: customKeyAcustomValueA\ncustomKeyBcustomValueB\n")
    def testMeshNextToProxyShapeAndImported(self):
        """
        Tests the colors between a mesh with (0.55, 0.55, 0.55)
        exporting that file and then re-importing it, and also referencing
        it back into the same scene through a proxy shape.

        While this is a bit more than just "GL" testing, it's a useful place to
        centralize all this.  If we don't like that this is testing usdImport
        functionality, we can remove

        This will render as follows:


        blank    |  usdImport
                 |
        ---------+-----------
        modeled  |  ref'd
                 |

        """
        x = self._PlaneWithColor((0.55, 0.55, 0.55))
        cmds.select(x)
        usdFile = os.path.join(self._testDir, 'plane.usd')
        cmds.mayaUSDExport(file=usdFile,
                           selection=True,
                           shadingMode='none',
                           exportDisplayColor=True)
        proxyShape = cmds.createNode('mayaUsdProxyShape', name='usdProxyShape')
        proxyTransform = cmds.listRelatives(proxyShape,
                                            parent=True,
                                            fullPath=True)[0]
        cmds.xform(proxyTransform, translation=(30.48, 0, 0))
        cmds.setAttr('%s.filePath' % proxyShape, usdFile, type='string')
        cmds.setAttr('%s.primPath' % proxyShape, '/pPlane1', type='string')

        x = cmds.mayaUSDImport(file=usdFile)
        cmds.xform(x, translation=(30.48, 30.48, 0))

        cmds.setAttr("hardwareRenderingGlobals.floatingPointRTEnable", 0)
        cmds.setAttr('defaultColorMgtGlobals.outputTransformEnabled', 0)
        self._Snapshot('default')
        cmds.setAttr("hardwareRenderingGlobals.floatingPointRTEnable", 1)
        cmds.setAttr('defaultColorMgtGlobals.outputTransformEnabled', 1)
        self._Snapshot('colorMgt')
示例#17
0
    def testComponentTags(self):
        """
        Test that component tags can round-trip through USD.
        """
        geo = cmds.polySphere(sx=6, sy=6)[0]
        geo_name = geo
        geo = geo_utils.extendToShape(geo)
        ctag_utils.createTag(geo, 'top', ['f[18:23]', 'f[30:35]'])
        ctag_utils.createTag(geo, 'bottom', ['f[0:5]', 'f[24:29]'])

        usdFilePath = os.path.join(os.environ.get('MAYA_APP_DIR'),
                                   'testComponentTags.usda')
        cmds.mayaUSDExport(mergeTransformAndShape=True,
                           file=usdFilePath,
                           shadingMode='none')

        cmds.file(new=True, force=True)
        rootPaths = cmds.mayaUSDImport(v=True, f=usdFilePath)
        self.assertEqual(len(rootPaths), 1)

        indices = cmds.getAttr('{0}.componentTags'.format(geo_name),
                               multiIndices=True) or []
        self.assertEqual(len(indices), 2)
示例#18
0
 def testUSDZImport(self):
     om.MFileIO.newFile(True)
     write_dir_path = os.path.dirname(self.usdz_path)
     cmds.mayaUSDImport(f=self.usdz_path, importUSDZTextures=True, importUSDZTexturesFilePath=write_dir_path)
     self.assertTrue(os.path.isfile(os.path.join(write_dir_path, "clouds_128_128.png")))
     self.assertTrue(os.path.isfile(os.path.join(write_dir_path, "red_128_128.png")))
    def testImportComplexOptimizedTest(self):
        """
        Testing a complex scenario, where one of the instances has been
        modified:

        Maya scene:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
            pCube2 [transform] (instance "source")
                pCubeShape2 [mesh] (instance "source")
            pCube3 [transform] (instance "source")
                pCubeShape2 (instance pCube1|pCube2|pCubeShape2)
        pCube4 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
        pCube5 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
            someUniqueChildOf_pCube5 [transform]

        We would expect the following unoptimized USD export to reload
        correctly:

        pCube1 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube4 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube5 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
            someUniqueChildOf_pCube5 [Xform]
        MayaExportedInstanceSources
            pCube1_pCubeShape1 [Scope]
                pCubeShape1 [Mesh]
            pCube1_pCube2 [Scope]
                pCube2 [Xform] (instance /MayaExportedInstanceSources/pCube1_pCube2_instancedParent)
            pCube1_pCube3 [Scope]
                pCube3 [Xform] (instance /MayaExportedInstanceSources/pCube1_pCube2_instancedParent)
            pCube1_instanceParent [Xform]
                pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
                pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
                pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
            pCube1_pCube2_instancedParent [Xform]
                pCubeShape2 [Mesh]

        Here we have created a simplified instance for children of pCube1 and
        pCube4, but kept unoptimized masters for pCube5. The instance
        pCube1_instanceParent is sharing the unoptimized instances.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "ComplexScenario_optimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # Correctly shared via instancing, but with 2 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube4|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube5|pCubeShape1|pCubeShape1|pCubeShape1Shape'),
            # The modified pCube5 does not share at the pCubeShape1 level:
            ('pCube1|pCubeShape1', 'pCube4|pCubeShape1'),

            ('pCube5|pCubeShape1',),
            # Correctly shared, with 2 extra transform:
            ('pCube1|pCube2|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube1|pCube3|pCube3|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube2|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube3|pCube3|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube2|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube3|pCube3|pCubeShape2|pCubeShape2Shape'),
            # All pCube2|pCube2 and pCube3|pCube3 are shared:
            ('pCube1|pCube2|pCube2', 'pCube4|pCube2|pCube2', 'pCube5|pCube2|pCube2',),

            ('pCube1|pCube3|pCube3', 'pCube4|pCube3|pCube3', 'pCube5|pCube3|pCube3',),
            # But at the pCube2 and pCube3 level the modified pCube5 is unshared:
            ('pCube1|pCube2', 'pCube4|pCube2'),

            ('pCube5|pCube2',),

            ('pCube1|pCube3', 'pCube4|pCube3'),

            ('pCube5|pCube3',),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
    def testImportComplexUnoptimizedTest(self):
        """
        Testing a complex scenario, where one of the instances has been
        modified:

        Maya scene:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
            pCube2 [transform] (instance "source")
                pCubeShape2 [mesh] (instance "source")
            pCube3 [transform] (instance "source")
                pCubeShape2 (instance pCube1|pCube2|pCubeShape2)
        pCube4 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
        pCube5 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
            someUniqueChildOf_pCube5 [transform]

        We would expect the following unoptimized USD export to reload
        correctly:

        pCube1 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
        pCube4 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
        pCube5 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
            someUniqueChildOf_pCube5 [Mesh]
        MayaExportedInstanceSources
            pCube1_pCubeShape1 [Scope]
                pCubeShape1 [Mesh]
            pCube1_pCube2 [Scope]
                pCube2 [Xform]
                    pCubeShape2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2_pCubeShape2)
            pCube1_pCube3 [Scope]
                pCube3 [Xform]
                    pCubeShape2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2_pCubeShape2)
            pCube1_pCube2_pCubeShape2 [Scope]
                pCubeShape2 [Mesh]

        There is some work to do to achieve that.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "ComplexScenario_unoptimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # Correctly shared via instancing, but with 2 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube4|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube5|pCubeShape1|pCubeShape1|pCubeShape1Shape'),
            # Correctly shared, but with 3 extra transforms:
            ('pCube1|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube1|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape'),
            # Incorrect: Would expect all pCube2 to be shared:
            ('pCube1|pCube2',),

            ('pCube4|pCube2',),

            ('pCube5|pCube2',),
            # Incorrect: Would expect all pCube3 to be shared:
            ('pCube1|pCube3',),

            ('pCube4|pCube3',),

            ('pCube5|pCube3',),
            # But at the pCube2|pCube2 level, we get correct sharing:
            ('pCube1|pCube2|pCube2',
             'pCube4|pCube2|pCube2',
             'pCube5|pCube2|pCube2'),
            # Same with pCube3|pCube3:
            ('pCube1|pCube3|pCube3',
             'pCube4|pCube3|pCube3',
             'pCube5|pCube3|pCube3'),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
    def testImportExportReworkOptimizedTest(self):
        """
        Testing another way testExportInstances could export to USD that would
        be easier to re-read. We would export optimized to:

        pCube1 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube4 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        pCube5 [Xform] (instance /MayaExportedInstanceSources/pCube1_instanceParent)
        MayaExportedInstanceSources
            pCube1_instanceParent [Xform]
                pCubeShape1 [Mesh]
                pCube2 [Xform] (instance /MayaExportedInstanceSources/pCube1_pCube2_instancedParent)
                pCube3 [Xform] (instance /MayaExportedInstanceSources/pCube1_pCube2_instancedParent)
            pCube1_pCube2_instancedParent [Xform]
                pCubeShape2 [Mesh]

        The optimization is detecting that all children of pCube1 are instances
        allowing a complex master to be created.

        The goal would be for Maya to end up with the same node hierarchy as
        the original scene:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
            pCube2 [transform] (instance "source")
                pCubeShape2 [mesh] (instance "source")
            pCube3 [transform] (instance "source")
                pCubeShape2 (instance pCube1|pCube2|pCubeShape2)
        pCube4 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
        pCube5 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)

        There is some work to do to achieve that.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "UsdExportInstancesTest_optimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # Correctly shared via instancing, but with 1 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1Shape',
             'pCube4|pCubeShape1|pCubeShape1Shape',
             'pCube5|pCubeShape1|pCubeShape1Shape'),
            # Correctly shared, with 1 extra transform:
            ('pCube1|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube1|pCube3|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube3|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube3|pCubeShape2|pCubeShape2Shape'),
            # All pCube2 are shared:
            ('pCube1|pCube2', 'pCube4|pCube2', 'pCube5|pCube2',),
            # All pCube3 are shared:
            ('pCube1|pCube3', 'pCube4|pCube3', 'pCube5|pCube3',)
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
    def testImportExportReworkUnoptimizedTest(self):
        """
        Testing another way testExportInstances could export to USD that would
        be easier to re-read. We would export unoptimized to:

        pCube1 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
        pCube4 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
        pCube5 [Xform]
            pCubeShape1 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCubeShape1)
            pCube2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2)
            pCube3 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube3)
        MayaExportedInstanceSources
            pCube1_pCubeShape1 [Scope]
                pCubeShape1 [Mesh]
            pCube1_pCube2 [Scope]
                pCube2 [Xform]
                    pCubeShape2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2_pCubeShape2)
            pCube1_pCube3 [Scope]
                pCube3 [Xform]
                    pCubeShape2 [Scope] (instance /MayaExportedInstanceSources/pCube1_pCube2_pCubeShape2)
            pCube1_pCube2_pCubeShape2 [Scope]
                pCubeShape2 [Mesh]

        The goal would be for Maya to end up with the same node hierarchy as
        the original scene by skipping all [Scope] primitives:

        pCube1 [transform]
            pCubeShape1 [mesh] (instance "source")
            pCube2 [transform] (instance "source")
                pCubeShape2 [mesh] (instance "source")
            pCube3 [transform] (instance "source")
                pCubeShape2 (instance pCube1|pCube2|pCubeShape2)
        pCube4 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)
        pCube5 [transform]
            pCubeShape1 [mesh] (instance pCube1|pCubeShape1)
            pCube2 [transform] (instance pCube1|pCube2)
            pCube3 [transform] (instance pCube1|pCube3)

        There is some work to do to achieve that.
        """
        usd_file = os.path.join(self._path, "UsdImportInstancesTest",
                                "UsdExportInstancesTest_unoptimized.usda")

        cmds.mayaUSDImport(file=usd_file,
                           primPath="/")

        # Format is sorted all paths of an instance clique:
        expected_cliques = [
            # Correctly shared via instancing, but with 2 extra transforms:
            ('pCube1|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube4|pCubeShape1|pCubeShape1|pCubeShape1Shape',
             'pCube5|pCubeShape1|pCubeShape1|pCubeShape1Shape'),
            # Correctly shared, but with 3 extra transforms:
            ('pCube1|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube1|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube4|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube2|pCube2|pCubeShape2|pCubeShape2|pCubeShape2Shape',
             'pCube5|pCube3|pCube3|pCubeShape2|pCubeShape2|pCubeShape2Shape'),
            # Incorrect: Would expect all pCube2 to be shared:
            ('pCube1|pCube2',),

            ('pCube4|pCube2',),

            ('pCube5|pCube2',),
            # Incorrect: Would expect all pCube3 to be shared:
            ('pCube1|pCube3',),

            ('pCube4|pCube3',),

            ('pCube5|pCube3',),
            # But at the pCube2|pCube2 level, we get correct sharing:
            ('pCube1|pCube2|pCube2',
             'pCube4|pCube2|pCube2',
             'pCube5|pCube2|pCube2'),
            # Same with pCube3|pCube3:
            ('pCube1|pCube3|pCube3',
             'pCube4|pCube3|pCube3',
             'pCube5|pCube3|pCube3'),
        ]

        for clique in expected_cliques:
            self.assertEqual(clique, self.sortedPathsTo(clique[0]))
示例#23
0
    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)