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

EMSUSD-671 fix reloading scene with layers saved in the Maya scene #3518

Merged
merged 1 commit into from
Dec 21, 2023
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
12 changes: 11 additions & 1 deletion lib/mayaUsd/nodes/proxyShapeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -940,8 +940,18 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock)

SdfLayerRefPtr rootLayer
= sharableStage ? computeRootLayer(dataBlock, fileString) : nullptr;
if (nullptr == rootLayer)
if (nullptr == rootLayer) {
rootLayer = SdfLayer::FindOrOpen(fileString);
} else {
// When reloading a Maya scene in which the root layer was saved in
// the scene, the root layer will be anonymous. In order for the next
// compute to find the root layer again, we need to set it as the
// _anonymousRootLayer, as done below when creating a new proxy shape
// and the root layer is initially created.
if (rootLayer->IsAnonymous()) {
_anonymousRootLayer = rootLayer;
}
}

if (nullptr == rootLayer) {
// Create an empty in-memory root layer so that a new stage in memory
Expand Down
1 change: 1 addition & 0 deletions test/lib/mayaUsd/nodes/ProxyShapeBaseTest/CubeModel.usda
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ def Xform "CubeModel" (
}
}
}

17 changes: 16 additions & 1 deletion test/lib/mayaUsd/nodes/testLayerManagerSerialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def setupEmptyScene(self):
cmds.file(rename=self._tempMayaFile)

def copyTestFilesAndMakeEdits(self):
'''
Copy an existing Maya scene that contains a stage and a few layer,
creates a few USD prim in the root, session and 1_1 layers.
'''
self._currentTestDir = tempfile.mkdtemp(prefix='LayerManagerTest')
fromDirectory = os.path.join(
self._inputPath, 'LayerManagerSerializationTest')
Expand All @@ -79,13 +83,20 @@ def copyTestFilesAndMakeEdits(self):
stage = mayaUsd.ufe.getStage(
"|SerializationTest|SerializationTestShape")
stack = stage.GetLayerStack()
# Note: layers are:
# 0: session
# 1: root
# 2: 1
# 3: 1_1
# 4: 2
# 5: 2_1
self.assertEqual(6, len(stack))

stage.SetEditTarget(stage.GetRootLayer())
newPrimPath = "/ChangeInRoot"
stage.DefinePrim(newPrimPath, "xform")

stage.SetEditTarget(stack[2])
stage.SetEditTarget(stack[3])
pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
newPrimPath = "/ChangeInLayer_1_1"
stage.DefinePrim(newPrimPath, "xform")

Expand All @@ -111,6 +122,10 @@ def copyTestFilesAndMakeEdits(self):
return stage

def confirmEditsSavedStatus(self, fileBackedSavedStatus, sessionSavedStatus):
'''
Clears the Maya scene, creates a new USD stage with the root layer
and verify various prim existence based on given flags.
'''
cmds.file(new=True, force=True)

proxyNode, stage = createProxyFromFile(self._rootUsdFile)
Expand Down
125 changes: 72 additions & 53 deletions test/lib/mayaUsd/nodes/testProxyShapeBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,46 @@ def testShareStagePreserveTCPS(self):
self.assertEqual(stage.GetTimeCodesPerSecond(), tcps)
self.assertEqual(stage.GetRootLayer().timeCodesPerSecond, tcps)

pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
def _getStage(self):
'''
Helper to get the stage, Needed since the stage instance will change
after saving.
'''
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
self.assertGreater(len(proxyShapes), 0)
proxyShapePath = proxyShapes[0]
return mayaUsd.lib.GetPrim(proxyShapePath).GetStage(), proxyShapePath

def _defineDummyPrim(self, stage = None, target = None):
'''
Define a prim named "dummy". Can be create in an edit target if given one.
'''
if stage is None:
stage, _ = self._getStage()
if target is not None:
stage.SetEditTarget(target)
return stage.DefinePrim("/dummy", "xform")

def _verifyPrim(self, isActive = True):
'''
Verify that the prim named "dummy" exists and its active state.
'''
stage, _ = self._getStage()
prim = stage.GetPrimAtPath("/dummy")
self.assertTrue(prim)
self.assertEqual(prim.IsActive(), isActive)

def _verifySubLayer(self, expectedCount = 3):
'''
Verify that the stage still contains a sub-layer under the root.
Can pass the expected count if the setup is modified, for example
when the stage is not shared, an extra layer added.
'''
stage, _ = self._getStage()
stack = stage.GetLayerStack()
# Layer stack: session, root, sub-layer
self.assertEqual(expectedCount, len(stack))

def testShareStagePreserveSession(self):
'''
Verify share/unshare stage preserves the data in the session layer
Expand All @@ -406,37 +446,23 @@ def testShareStagePreserveSession(self):
# Open usdCylinder.ma scene in testSamples
mayaUtils.openCylinderScene()

# get the stage
def getStage():
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
self.assertGreater(len(proxyShapes), 0)
proxyShapePath = proxyShapes[0]
return mayaUsd.lib.GetPrim(proxyShapePath).GetStage(), proxyShapePath

# check that the stage is shared and the root is the right one
stage, proxyShapePath = getStage()
stage, proxyShapePath = self._getStage()
self.assertTrue(cmds.getAttr('{}.{}'.format(proxyShapePath,"shareStage")))

# create a prim in the session layer.
stage.SetEditTarget(stage.GetSessionLayer())
stage.DefinePrim("/dummy", "xform")

# verify that the prim exists.
def verifyPrim():
stage, _ = getStage()
self.assertTrue(stage.GetPrimAtPath("/dummy"))

verifyPrim()
self._defineDummyPrim(stage, stage.GetSessionLayer())
self._verifyPrim()

# unshare the stage and verify the prim in the session layer still exists.
cmds.setAttr('{}.{}'.format(proxyShapePath,"shareStage"), False)

verifyPrim()
self._verifyPrim()

# re-share the stage and verify the prim in the session layer still exists.
cmds.setAttr('{}.{}'.format(proxyShapePath,"shareStage"), True)

verifyPrim()
self._verifyPrim()

def _saveStagePreserveLayerHelper(self, targetRoot, saveInMaya):
'''
Expand All @@ -451,34 +477,23 @@ def _saveStagePreserveLayerHelper(self, targetRoot, saveInMaya):
cmds.file(new=True, force=True)
mayaUtils.createProxyAndStage()

# Helper to get the stage, Needed since the stage instance will change
# after saving.
def getStage():
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
self.assertGreater(len(proxyShapes), 0)
proxyShapePath = proxyShapes[0]
return mayaUsd.lib.GetPrim(proxyShapePath).GetStage(), proxyShapePath
stage, proxyShapePath = self._getStage()

stage, proxyShapePath = getStage()
# Create an anonymous sub-layer.
subLayer = Sdf.Layer.CreateAnonymous("middleLayer")
stage.GetRootLayer().subLayerPaths = [subLayer.identifier]

# Create a prim in the root layer.
stage.SetEditTarget(stage.GetRootLayer())
stage.DefinePrim("/dummy", "xform")

# Make the prim inactive in the desired target layer.
target = stage.GetRootLayer() if targetRoot else stage.GetSessionLayer()
stage.SetEditTarget(target)
prim = stage.GetPrimAtPath("/dummy")
prim = self._defineDummyPrim(stage, target)
prim.SetActive(False)

# verify that the prim exists but is inactive.
def verifyPrim():
stage, _ = getStage()
prim = stage.GetPrimAtPath("/dummy")
self.assertIsNotNone(prim)
self.assertFalse(prim.IsActive())

verifyPrim()
self._verifyPrim(False)

# Temp file names for Maya scene and USD file.
with testUtils.TemporaryDirectory(prefix='ProxyShapeBase', ignore_errors=True) as testDir:
Expand All @@ -496,8 +511,24 @@ def verifyPrim():
cmds.file(save=True, force=True, type='mayaAscii')

# Verify that the prim is still inactive in the target layer.
self._verifyPrim(False)
self._verifySubLayer()

# Reload the file and verify again.
cmds.file(new=True, force=True)
cmds.file(tempMayaFile, force=True, open=True)

verifyPrim()
self._verifyPrim(False)
self._verifySubLayer()

# Change shared status and verify again. Changing the shared flag recomputes
# the stage and its layer, which is what we really want to test here. So we
# change an input attribute that we know will recompute the layers.
cmds.setAttr('{}.{}'.format(proxyShapePath,"shareStage"), False)

pierrebai-adsk marked this conversation as resolved.
Show resolved Hide resolved
# Verify that the prim is still inactive in the target layer.
self._verifyPrim(False)
self._verifySubLayer(4)

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

Expand Down Expand Up @@ -537,31 +568,19 @@ def testUnsavedStagePreserveRootLayerWhenUpdated(self):
cmds.file(new=True, force=True)
mayaUtils.createProxyAndStage()

# Helper to get the stage,
def getStage():
proxyShapes = cmds.ls(type="mayaUsdProxyShapeBase", long=True)
self.assertGreater(len(proxyShapes), 0)
proxyShapePath = proxyShapes[0]
return mayaUsd.lib.GetPrim(proxyShapePath).GetStage(), proxyShapePath

# create a prim in the root layer.
stage, proxyShapePath = getStage()
stage.SetEditTarget(stage.GetRootLayer())
stage.DefinePrim("/dummy", "xform")
self._defineDummyPrim()

# verify that the prim exists.
def verifyPrim():
stage, _ = getStage()
self.assertTrue(stage.GetPrimAtPath("/dummy"))

verifyPrim()
self._verifyPrim()

# Set an attribute on the proxy shape. Here we set the loadPayloads.
# It was already set, this only triggers a Maya node recompute.
_, proxyShapePath = self._getStage()
cmds.setAttr('{}.{}'.format(proxyShapePath,"loadPayloads"), True)

# Verify that we did not lose the data on the root layer.
verifyPrim()
self._verifyPrim()

def testSettingStageViaIdPreservedWhenSaved(self):
'''
Expand Down