Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial cache to USD test. #2081

Merged
merged 1 commit into from
Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/mayaUsd/resources/scripts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ list(APPEND scripts_src
mayaUsdMayaReferenceUtils.py
)

list(APPEND maya_lib_scripts_src
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USD cache creation options moved out of UI code to separate Python module, under mayaUsd.lib

cacheToUsd.py
)

install(FILES ${scripts_src}
DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/scripts
)

set(PYTHON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib/python/${PROJECT_NAME}/lib)
install(FILES ${maya_lib_scripts_src} DESTINATION ${PYTHON_INSTALL_PREFIX})
47 changes: 47 additions & 0 deletions lib/mayaUsd/resources/scripts/cacheToUsd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright 2022 Autodesk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import mayaUsd
import maya.cmds as cmds

import re

def turnOnAnimation(textOptions):
return re.sub(r'animation=.', 'animation=1', textOptions)

def getDefaultExportOptions():
# Animation is always on for cache to USD.
return turnOnAnimation(cmds.translator('USD Export', query=True, do=True))

def getCacheCreationOptions(exportOptions, cacheFile, cachePrimName,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utilities to centralize cache option creation in Python.

payloadOrReference, listEditType,
variantSetName = None, variantName = None):

defineInVariant = 0 if variantSetName is None else 1

userArgs = mayaUsd.lib.Util.getDictionaryFromEncodedOptions(exportOptions)

userArgs['rn_layer'] = cacheFile
userArgs['rn_primName'] = cachePrimName
userArgs['rn_defineInVariant'] = defineInVariant
userArgs['rn_payloadOrReference'] = payloadOrReference
userArgs['rn_listEditType'] = listEditType

if defineInVariant:
userArgs['rn_variantSetName'] = variantSetName
userArgs['rn_variantName'] = variantName

return userArgs
49 changes: 24 additions & 25 deletions lib/mayaUsd/resources/scripts/mayaUsdCacheMayaReference.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import maya.mel as mel

import mayaUsd
from mayaUsd.lib import cacheToUsd

from mayaUsdLibRegisterStrings import getMayaUsdLibString
import mayaUsdMayaReferenceUtils as mayaRefUtils
Expand All @@ -32,7 +33,7 @@
kDefaultCachePrimName = 'Cache1'

# Cache options in string format, for MEL mayaUsdTranslatorExport() consumption.
_cacheOptions = None
_cacheExportOptions = None

# Dag path corresponding to pulled prim. This is a Maya transform node that is
# not in the Maya reference itself, but is its parent.
Expand Down Expand Up @@ -181,22 +182,22 @@ def fileOptionsTabPage(tabLayout):
# USD file option controls will be parented under this layout.
# resultCallback not called on "post", is therefore an empty string.
fileOptionsScroll = cmds.columnLayout('fileOptionsScroll')
mel.eval('mayaUsdTranslatorExport("fileOptionsScroll", "post=all;!animation-data", "' + getCacheOptions() + '", "")')
mel.eval('mayaUsdTranslatorExport("fileOptionsScroll", "post=all;!animation-data", "' + getCacheExportOptions() + '", "")')

cacheFileUsdHierarchyOptions(topForm)

cmds.setUITemplate(popTemplate=True)

def getCacheOptions():
global _cacheOptions
# Init on first use from "USD Export" translator.
if _cacheOptions is None:
_cacheOptions = cmds.translator('USD Export', query=True, do=True)
return _cacheOptions
def getCacheExportOptions():
global _cacheExportOptions
if _cacheExportOptions is None:
_cacheExportOptions = cacheToUsd.getDefaultExportOptions()
return _cacheExportOptions

def setCacheOptions(newCacheOptions):
global _cacheOptions
_cacheOptions = newCacheOptions
global _cacheExportOptions
# Animation is always on for cache to USD.
_cacheExportOptions = cacheToUsd.turnOnAnimation(newCacheOptions)

def cacheCreateUi(parent):
cmds.setParent(parent)
Expand Down Expand Up @@ -239,29 +240,27 @@ def cacheInitUi(parent, filterType):
def cacheCommitUi(parent, selectedFile):
# Read data to set up cache.

# The following call will set _cacheOptions. Initial settings not accessed
# on "query", is therefore an empty string.
# The following call will set _cacheExportOptions. Initial settings not
# accessed on "query", is therefore an empty string.
mel.eval('mayaUsdTranslatorExport("fileOptionsScroll", "query", "", "mayaUsdCacheMayaReference_setCacheOptions")')

# Regardless of UI, animation is always on.
cacheOptionsText = re.sub(r'animation=.', 'animation=1', getCacheOptions())

userArgs = mayaUsd.lib.Util.getDictionaryFromEncodedOptions(cacheOptionsText)

primName = cmds.textFieldGrp('primNameText', query=True, text=True)
defineInVariant = cmds.radioButtonGrp('variantRadioBtn', query=True, select=True)
userArgs['rn_layer'] = selectedFile
userArgs['rn_primName'] = primName
userArgs['rn_defineInVariant'] = defineInVariant
userArgs['rn_payloadOrReference'] = cmds.optionMenuGrp('compositionArcTypeMenu', query=True, value=True)
userArgs['rn_listEditType'] = cmds.optionMenu('listEditedAsMenu', query=True, value=True)
payloadOrReference = cmds.optionMenuGrp('compositionArcTypeMenu', query=True, value=True)
listEditType = cmds.optionMenu('listEditedAsMenu', query=True, value=True)

defineInVariant = cmds.radioButtonGrp('variantRadioBtn', query=True, select=True)
if defineInVariant:
userArgs['rn_variantSetName'] = cmds.optionMenu('variantSetMenu', query=True, value=True)
variantSetName = cmds.optionMenu('variantSetMenu', query=True, value=True)
variantName = cmds.optionMenu('variantNameMenu', query=True, value=True)
if variantName == 'Create New':
variantName = cmds.textField('variantNameText', query=True, text=True)
userArgs['rn_variantName'] = variantName
else:
variantName = None
variantSetName = None

userArgs = cacheToUsd.getCacheCreationOptions(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use new utility functions.

getCacheExportOptions(), selectedFile, primName, payloadOrReference,
listEditType, variantSetName, variantName)

# Call push.
if not mayaUsd.lib.PrimUpdaterManager.mergeToUsd(_mayaRefDagPath, userArgs):
Expand Down
1 change: 1 addition & 0 deletions test/lib/mayaUsd/fileio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ if(CMAKE_UFE_V3_FEATURES_AVAILABLE)
testDiscardEdits.py
testDuplicateAs.py
testPrimUpdater.py
testCacheToUsd.py
)
endif()

Expand Down
20 changes: 3 additions & 17 deletions test/lib/mayaUsd/fileio/testAddMayaReference.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ class AddMayaReferenceTestCase(unittest.TestCase):

@classmethod
def setUpClass(cls):
fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False)
if not cls.pluginsLoaded:
cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded()
fixturesUtils.setUpClass(__file__)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create a Maya file in a temporary directory, using the fixture utils sets up a temporary directory for the test, and clears it out before running. Just use setUpClass() instead of readOnlySetUpClass(). Also (have seen this in a few places) why pass in loadPlugin=False to the fixturesUtils, then load the plugin separately? Just use the fixturesUtils service.


# Create a pure Maya scene to reference in.
cls.mayaSceneStr = cls.createSimpleMayaScene()
import os
cls.mayaSceneStr = mayaUtils.createSingleSphereMayaScene(os.getcwd())

@classmethod
def tearDownClass(cls):
Expand All @@ -58,19 +57,6 @@ def setUp(self):
self.proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
self.stage = mayaUsd.lib.GetPrim(self.proxyShapePathStr).GetStage()

@staticmethod
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factored out to mayaUtils.py for re-use.

def createSimpleMayaScene():
import os
import maya.cmds as cmds
import tempfile

cmds.file(new=True, force=True)
cmds.CreatePolygonSphere()
tempMayaFile = os.path.join(tempfile.gettempdir(), 'simpleSphere.ma')
cmds.file(rename=tempMayaFile)
cmds.file(save=True, force=True, type='mayaAscii')
return tempMayaFile

def testDefault(self):
'''Test the default options for Add Maya Reference.

Expand Down
199 changes: 199 additions & 0 deletions test/lib/mayaUsd/fileio/testCacheToUsd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#!/usr/bin/env python

#
# Copyright 2022 Autodesk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import fixturesUtils

from mayaUtils import setMayaTranslation, setMayaRotation
from usdUtils import createSimpleXformScene, mayaUsd_createStageWithNewLayer
from ufeUtils import createItem, createHierarchy

import mayaUsd.lib

from mayaUsd.lib import cacheToUsd

import mayaUtils
import mayaUsd.ufe
import mayaUsdAddMayaReference
import mayaUsdMayaReferenceUtils as mayaRefUtils

from pxr import Usd, UsdGeom, Sdf, Pcp

from maya import cmds
from maya import standalone
from maya.api import OpenMaya as om

import ufe

import unittest

from testUtils import assertVectorAlmostEqual, getTestScene

import os

class CacheToUsdTestCase(unittest.TestCase):
'''Test cache to USD: commit edits done to a Maya reference back into USD.
'''

kDefaultNamespace = 'simpleSphere'

@classmethod
def setUpClass(cls):
fixturesUtils.setUpClass(__file__)

# Create a pure Maya scene to reference in.
cls.mayaSceneStr = mayaUtils.createSingleSphereMayaScene()

@classmethod
def tearDownClass(cls):
standalone.uninitialize()

def setUp(self):
# Start each test with a new scene with empty stage.
cmds.file(new=True, force=True)
import mayaUsd_createStageWithNewLayer
self.proxyShapePathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
self.stage = mayaUsd.lib.GetPrim(self.proxyShapePathStr).GetStage()

def testCacheToUsd(self):
'''Cache edits on a pulled Maya reference prim back to USD.'''

# Create a Maya reference prim using the defaults, under a
# newly-created parent, without any variant sets.
cacheParent = self.stage.DefinePrim('/CacheParent', 'Xform')
cacheParentPathStr = self.proxyShapePathStr + ',/CacheParent'
self.assertFalse(cacheParent.HasVariantSets())

mayaRefPrim = mayaUsdAddMayaReference.createMayaReferencePrim(
cacheParentPathStr, self.mayaSceneStr, self.kDefaultNamespace)

# The Maya reference prim is a child of the cache parent.
cacheParentItem = createItem(cacheParentPathStr)
mayaRefPrimPathStr = cacheParentPathStr + '/' + mayaRefPrim.GetName()
mayaRefPrimItem = createItem(mayaRefPrimPathStr)
cacheParentHier = ufe.Hierarchy.hierarchy(cacheParentItem)
cacheParentChildren = cacheParentHier.children()

self.assertTrue(len(cacheParentChildren), 1)
self.assertEqual(cacheParentChildren[0], mayaRefPrimItem)

# At this point no Maya reference node has been created.
self.assertEqual(len(cmds.ls(type='reference')), 0)

# testAddMayaReference tests Maya reference prim creation, so we do not
# repeat these tests here. Edit the Maya reference prim as Maya, which
# will create and load the corresponding Maya reference node.
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(
mayaRefPrimPathStr))

# A Maya reference node has been created, and it is loaded.
rns = cmds.ls(type='reference')
rn = rns[0]
self.assertEqual(len(rns), 1)
self.assertTrue(cmds.referenceQuery(rn, isLoaded=True))

# The Maya reference prim has been replaced by a Maya transform, which
# is a child of the cache parent.
cacheParentChildren = cacheParentHier.children()
self.assertTrue(len(cacheParentChildren), 1)
mayaTransformItem = cacheParentChildren[0]
self.assertEqual(mayaTransformItem.nodeType(), 'transform')

# The child of the Maya transform is the top-level transform of the
# referenced Maya scene.
mayaTransformHier = createHierarchy(mayaTransformItem)
mayaTransformChildren = mayaTransformHier.children()
self.assertTrue(len(mayaTransformChildren), 1)
sphereTransformItem = mayaTransformChildren[0]
self.assertEqual(sphereTransformItem.nodeType(), 'transform')

# As the sphereTransformItem is a pulled node, its path is a pure Maya
# Dag path. Confirm that it is a referenced node.
sphereTransformPath = sphereTransformItem.path()
self.assertEqual(sphereTransformPath.nbSegments(), 1)
self.assertEqual(sphereTransformPath.runTimeId(), 1)
sphereTransformPathStr = ufe.PathString.string(sphereTransformPath)
sphereTransformObj = om.MSelectionList().add(sphereTransformPathStr).getDagPath(0).node()
self.assertTrue(om.MFnDependencyNode(sphereTransformObj).isFromReferencedFile)

# Make an edit in Maya.
(_, _, _, mayaMatrix) = \
setMayaTranslation(sphereTransformItem, om.MVector(4, 5, 6))

# Cache to USD, using a sibling prim and a payload composition arc.
defaultExportOptions = cacheToUsd.getDefaultExportOptions()
cacheFile = 'testCacheToUsd.usda'
cachePrimName = 'cachePrimName'
payloadOrReference = 'Payload'
listEditType = 'Prepend'
cacheOptions = cacheToUsd.getCacheCreationOptions(
defaultExportOptions, cacheFile, cachePrimName,
payloadOrReference, listEditType)

# Before caching, the cache file does not exist.
self.assertFalse(os.path.exists(cacheFile))

# Cache edits back to USD.
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.mergeToUsd(ufe.PathString.string(mayaTransformItem.path()), cacheOptions))

# The Maya reference node has been unloaded.
self.assertFalse(cmds.referenceQuery(rn, isLoaded=True))

# The USD cache file has been saved to the file system.
self.assertTrue(os.path.exists(cacheFile))

# There is a new child under the cache parent, with the chosen cache
# prim name.
cacheParentChildren = cacheParentHier.children()
self.assertTrue(len(cacheParentChildren), 2)
cacheItem = next((c for c in cacheParentChildren if c.nodeName() == cachePrimName), None)
self.assertIsNotNone(cacheItem)

# The Maya transformation has been transferred to the USD cache, in the
# sphere child of the cache item.
cacheHier = createHierarchy(cacheItem)
cacheChildren = cacheHier.children()
sphereItem = next((c for c in cacheChildren if c.nodeType() == 'Mesh'), None)
self.assertIsNotNone(sphereItem)

sphereItemPathStr = ufe.PathString.string(sphereItem.path())
spherePrim = mayaUsd.ufe.ufePathToPrim(sphereItemPathStr)
sphereXformOps = UsdGeom.Xformable(spherePrim).GetOrderedXformOps()
self.assertEqual(len(sphereXformOps), 1)
sphereXformOp = sphereXformOps[0]
usdMatrix = sphereXformOp.GetOpTransform(mayaUsd.ufe.getTime(sphereItemPathStr))

mayaValues = [v for v in mayaMatrix]
usdValues = [v for row in usdMatrix for v in row]

assertVectorAlmostEqual(self, mayaValues, usdValues)

# The cache prim has a payload composition arc.
cachePrim = mayaUsd.ufe.ufePathToPrim(ufe.PathString.string(cacheItem.path()))
query = Usd.PrimCompositionQuery(cachePrim)
foundPayload = False
for arc in query.GetCompositionArcs():
if arc.GetArcType() == Pcp.ArcTypePayload:
foundPayload = True
break

self.assertTrue(foundPayload)

if __name__ == '__main__':
unittest.main(verbosity=2)
Loading