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

MAYA-109647 - Target becomes out of synch when an ancestor of a targe… #1156

Merged
merged 2 commits into from
Feb 10, 2021
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
67 changes: 58 additions & 9 deletions lib/mayaUsd/commands/layerEditorCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,64 @@ class InsertRemoveSubPathBase : public BaseCmd
_subPath = layer->GetSubLayerPaths()[_index];
holdOnPathIfDirty(layer, _subPath);

// if the layer to remove is the current edit target,
// if the current edit target is the layer to remove or
// a sublayer of the layer to remove,
// set the root layer as the current edit target
auto subLayerHandle = SdfLayer::FindRelativeToLayer(layer, _subPath);
auto layerToRemove = SdfLayer::FindRelativeToLayer(layer, _subPath);
auto stage = getStage();
auto currentTarget = stage->GetEditTarget().GetLayer();
if (currentTarget && subLayerHandle
&& currentTarget->GetIdentifier() == subLayerHandle->GetIdentifier()) {
_isEditTarget = true;
stage->SetEditTarget(stage->GetRootLayer());

// Helper function to find if a layer is in the
// hierarchy of another layer
//
// rootLayer: The root layer of the hierarchy
// layer: The layer to find
// ignore : Optional layer used has the root of a hierarchy that
// we don't want to check in.
// ignoreSubPath : Optional subpath used whith ignore layer.
auto isInHierarchy = [](const SdfLayerHandle& rootLayer,
const SdfLayerHandle& layer,
const SdfLayerHandle* ignore = nullptr,
const std::string* ignoreSubPath = nullptr) {
// Impl used for recursive call
auto isInHierarchyImpl = [](const SdfLayerHandle& rootLayer,
const SdfLayerHandle& layer,
const SdfLayerHandle* ignore,
const std::string* ignoreSubPath,
auto& implRef) {
if (!rootLayer || !layer)
return false;

if (rootLayer->GetIdentifier() == layer->GetIdentifier())
return true;

const auto subLayerPaths = rootLayer->GetSubLayerPaths();
for (const auto& subLayerPath : subLayerPaths) {

if (ignore && ignoreSubPath
&& (*ignore)->GetIdentifier() == rootLayer->GetIdentifier()
&& *ignoreSubPath == subLayerPath)
continue;

const auto subLayer
= SdfLayer::FindRelativeToLayer(rootLayer, subLayerPath);
if (implRef(subLayer, layer, ignore, ignoreSubPath, implRef))
return true;
}
return false;
};
return isInHierarchyImpl(
rootLayer, layer, ignore, ignoreSubPath, isInHierarchyImpl);
};

if (isInHierarchy(layerToRemove, currentTarget)) {
// The current edit layer is in the hierarchy of the layer to remove,
// now we need to be sure the edit target layer is not also a sublayer
// of another layer in the stage.
if (!isInHierarchy(stage->GetRootLayer(), currentTarget, &layer, &_subPath)) {
_editTargetPath = currentTarget->GetIdentifier();
stage->SetEditTarget(stage->GetRootLayer());
}
}

layer->RemoveSubLayerPath(_index);
Expand All @@ -181,9 +230,9 @@ class InsertRemoveSubPathBase : public BaseCmd

// if the removed layer was the edit target,
// set it back to the current edit target
if (_isEditTarget) {
if (!_editTargetPath.empty()) {
auto stage = getStage();
auto subLayerHandle = SdfLayer::FindRelativeToLayer(layer, _subPath);
auto subLayerHandle = SdfLayer::FindRelativeToLayer(layer, _editTargetPath);
stage->SetEditTarget(subLayerHandle);
}
} else {
Expand All @@ -210,7 +259,7 @@ class InsertRemoveSubPathBase : public BaseCmd
}

protected:
bool _isEditTarget = false;
std::string _editTargetPath;

UsdStageWeakPtr getStage()
{
Expand Down
103 changes: 103 additions & 0 deletions test/lib/testMayaUsdLayerEditorCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import unittest
import tempfile
from os import path
from maya import cmds
import mayaUsd_createStageWithNewLayer
import mayaUsd
Expand Down Expand Up @@ -262,3 +263,105 @@ def testSubLayerEditing(self):
self.assertEqual(rootLayer.subLayerPaths[0], childLayerId)
childLayer = Sdf.Layer.Find(childLayerId)
self.assertEqual(childLayer.subLayerPaths[0], grandChildLayerId)

def testRemoveEditTarget(self):

shapePath, stage = getCleanMayaStage()
rootLayer = stage.GetRootLayer()
rootLayerID = rootLayer.identifier

def setAndRemoveEditTargetRecursive(layer, index, editLayer):
cmds.mayaUsdEditTarget(shapePath, edit=True, editTarget=editLayer)
cmds.mayaUsdLayerEditor(layer.identifier, edit=True, removeSubPath=[index,shapePath])
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, rootLayerID)
cmds.undo()
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, editLayer)

subLayer = Sdf.Layer.FindRelativeToLayer(layer, editLayer)
for subLayerOffset, subLayerId in enumerate(subLayer.subLayerPaths):
setAndRemoveEditTargetRecursive(subLayer, subLayerOffset, subLayerId)

# build a layer hierarchy
layerColorId = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="Color")[0]
myLayerId = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="MyLayer")[0]

layerRedId = cmds.mayaUsdLayerEditor(layerColorId, edit=True, addAnonymous="Red")[0]
layerGreen = cmds.mayaUsdLayerEditor(layerColorId, edit=True, addAnonymous="Green")[0]
layerBlue = cmds.mayaUsdLayerEditor(layerColorId, edit=True, addAnonymous="Blue")[0]

layerDarkRed = cmds.mayaUsdLayerEditor(layerRedId, edit=True, addAnonymous="DarkRed")[0]
layerLightRed = cmds.mayaUsdLayerEditor(layerRedId, edit=True, addAnonymous="LightRed")[0]

mySubLayerId = cmds.mayaUsdLayerEditor(myLayerId, edit=True, addAnonymous="MySubLayer")[0]

# traverse the layer tree
# for each layer, set it as the edit target and remove it.
for subLayerOffset, subLayerPath in enumerate(rootLayer.subLayerPaths):
setAndRemoveEditTargetRecursive(rootLayer, subLayerOffset, subLayerPath)

#
# Test when the editTarget's parent (direct/ indirect) layer is removed
#
cmds.mayaUsdEditTarget(shapePath, edit=True, editTarget=layerDarkRed)

# remove the Red layer (direct parent of DarkRed layer)
cmds.mayaUsdLayerEditor(layerColorId, edit=True, removeSubPath=[2,shapePath])
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, rootLayerID)
cmds.undo()
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, layerDarkRed)

# remove the Color layer (indirect parent of DarkRed layer)
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, removeSubPath=[1,shapePath])
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, rootLayerID)
cmds.undo()
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, layerDarkRed)

#
# Test with a layer that is a sublayer multiple times.
#
sharedLayerFile = tempfile.NamedTemporaryFile(suffix=".usda", prefix="sharedLayer", delete=False, mode="w")
sharedLayerFile.write("#usda 1.0")
sharedLayerFile.close()

sharedLayer = path.normcase(sharedLayerFile.name)

cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, insertSubPath=[0, sharedLayer])
cmds.mayaUsdLayerEditor(mySubLayerId, edit=True, insertSubPath=[0, sharedLayer])

cmds.mayaUsdEditTarget(shapePath, edit=True, editTarget=sharedLayer)

# remove the sharedLayer under the root layer.
# the edit target should still be the shared layer
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, removeSubPath=[0,shapePath])
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)

# remove the sharedLayer under the MySubLayer.
# the edit target should still be the shared layer
cmds.mayaUsdLayerEditor(mySubLayerId, edit=True, removeSubPath=[0,shapePath])
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)

# remove MySubLayer (Direct parent of SharedLayer).
# the edit target should still be the shared layer
cmds.mayaUsdLayerEditor(myLayerId, edit=True, removeSubPath=[0,shapePath])
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)

# remove MyLayer (Indirect parent of SharedLayer).
# the edit target should still be the shared layer
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, removeSubPath=[0,shapePath])
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)

# remove SharedLayer everywhere.
# the edit target should become the root layer
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, removeSubPath=[0,shapePath])
cmds.mayaUsdLayerEditor(mySubLayerId, edit=True, removeSubPath=[0,shapePath])
self.assertEqual(stage.GetEditTarget().GetLayer().identifier, rootLayerID)
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)