Skip to content

Commit

Permalink
Merge pull request #3518 from Autodesk/bailp/EMSUSD-671/fix-lost-layers
Browse files Browse the repository at this point in the history
EMSUSD-671 fix reloading scene with layers saved in the Maya scene
  • Loading branch information
seando-adsk authored Dec 21, 2023
2 parents 7828136 + b8672f5 commit 71d74e7
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 55 deletions.
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])
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)

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)

# 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

0 comments on commit 71d74e7

Please sign in to comment.