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-104215: improve renaming restriction on prim objects #475

Merged
merged 30 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c9100f7
MAYA-104215: Trying to rename an object that was not defined on the t…
Apr 29, 2020
54166d2
Address feedbacks:
Apr 30, 2020
8f8295f
First attempt to handle following scenario:
May 1, 2020
e21a130
Fix compiler errors.
May 4, 2020
4c67b69
Address Feedbacks: rename functions and variables to read better.
May 4, 2020
97996bb
- Added two new test cases for rename restriction operations.
May 5, 2020
581932b
Address feedback: rename test case names to something more meaningful.
May 5, 2020
761143d
Fix MacOS build and make sure to pass the error messages by char*
May 5, 2020
d58a028
Fix debug build:
May 5, 2020
20be9ac
MAYA-104215: fix the "cruft" in USD files as objects are named and re…
May 6, 2020
ae2a7e0
Improve rename logics:
May 6, 2020
bb1da2e
- Added early checks to make sure we are in a valid state when doing …
May 6, 2020
c4f247a
- Added tree.ma and tree.usda to the test-samples. This is a useful u…
May 6, 2020
a45779c
Updated test prim. tesRename does pass but there are still some issue…
May 7, 2020
b207895
- Add new logics for handling internal vs external references.
May 11, 2020
890372d
Eliminate the need to stores _prim and _oldName member variables.
May 12, 2020
a305af9
Address feedback: Re-factored the logics for detecting internal vs ex…
May 12, 2020
610f2ce
Address feedback: break out of the loop once the condition is met.
May 12, 2020
80aa35e
Address feedback: Don't return if we have an internal reference since…
May 12, 2020
3cf2acc
- change the wording form opinion to contribute/contribution to make …
May 13, 2020
7bb77b3
Merge pull request #483 from Autodesk/sabrih/MAYA-104215/improve_rena…
HamedSabri-adsk May 13, 2020
24001e3
Merge branch 'dev' into sabrih/MAYA-104215/improve_usd_rename_operation
HamedSabri-adsk May 13, 2020
7e66d41
Fix build after merging dev branch into this PR.
May 13, 2020
22d6fc9
Fix reference file path.
May 13, 2020
df3ff44
Make sure maya scene examples are using relative path.
May 13, 2020
67fd431
Nit-pick: No need to pass stage as an argument since it can be retrie…
May 14, 2020
7e8bef6
Added more logics for handling defaultprim rename.
May 14, 2020
d84d82f
Nit-Pick: do an object comparison instead of string.
May 14, 2020
5d81f6c
For future reference, added the feedback I received from Spiff under …
May 15, 2020
ade83fa
Remove out of date comment. MAYA-92264 is now internally closed.
May 15, 2020
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
14 changes: 7 additions & 7 deletions lib/mayaUsd/ufe/UsdUndoRenameCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ UsdUndoRenameCommand::UsdUndoRenameCommand(const UsdSceneItem::Ptr& srcItem, con
_ufeSrcItem = srcItem;
_usdSrcPath = prim.GetPath();

// Every call to rename() (through execute(), undo() or redo()) removes
// Every call to rename() (through execute(), undo() or redo()) removes
// a prim, which becomes expired. Since USD UFE scene items contain a
// prim, we must recreate them after every call to rename.
_usdDstPath = prim.GetParent().GetPath().AppendChild(TfToken(newName.string()));
Expand All @@ -59,7 +59,7 @@ UsdUndoRenameCommand::UsdUndoRenameCommand(const UsdSceneItem::Ptr& srcItem, con
}

// if the current layer doesn't have any opinions that affects selected prim
if (!MayaUsdUtils::doesLayerHavePrimSpec(prim)) {
if (!MayaUsdUtils::doesEditTargetLayerHavePrimSpec(prim)) {
auto possibleTargetLayer = MayaUsdUtils::strongestLayerWithPrimSpec(prim);
std::string err = TfStringPrintf("Cannot rename [%s] defined on another layer. "
"Please set [%s] as the target layer to proceed",
Expand All @@ -69,16 +69,16 @@ UsdUndoRenameCommand::UsdUndoRenameCommand(const UsdSceneItem::Ptr& srcItem, con
}
Copy link
Contributor Author

@HamedSabri-adsk HamedSabri-adsk Apr 29, 2020

Choose a reason for hiding this comment

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

We check here if the target layer has any opinions that affects selected prim. If it doesn't pass the check, we then find the possible target layer and append it's display name to the error message. Finally we throw a runtime_error message.

else
{
auto layers = MayaUsdUtils::layersWithOpinion(prim);
auto layers = MayaUsdUtils::layersWithPrimSpec(prim);

if (layers.size() > 1) {
std::string layerNames;
std::string layerDisplayNames;
for (auto layer : layers) {
layerNames.append("[" + layer->GetDisplayName() + "]" + ",");
layerDisplayNames.append("[" + layer->GetDisplayName() + "]" + ",");
}
layerNames.pop_back();
layerDisplayNames.pop_back();
std::string err = TfStringPrintf("Cannot rename [%s] with definitions or opinions on other layers. "
"Opinions exist in %s", prim.GetName().GetString(), layerNames);
"Opinions exist in %s", prim.GetName().GetString(), layerDisplayNames);
throw std::runtime_error(err.c_str());
}
}
Expand Down
12 changes: 6 additions & 6 deletions lib/usd/utils/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,22 @@ defPrimSpecLayer(const UsdPrim& prim)
}

std::vector<SdfLayerHandle>
layersWithOpinion(const UsdPrim& prim)
layersWithPrimSpec(const UsdPrim& prim)
{
// get the list of PrimSpecs that provide opinions for this prim
// ordered from strongest to weakest opinion.
// ordered from strongest to weakest.
const auto& primStack = prim.GetPrimStack();

std::vector<SdfLayerHandle> layersWithOpion;
std::vector<SdfLayerHandle> layersWithPrimSpec;
for (auto primSpec : primStack) {
layersWithOpion.emplace_back(primSpec->GetLayer());
layersWithPrimSpec.emplace_back(primSpec->GetLayer());
}

return layersWithOpion;
return layersWithPrimSpec;
}

bool
doesLayerHavePrimSpec(const UsdPrim& prim)
doesEditTargetLayerHavePrimSpec(const UsdPrim& prim)
{
auto editTarget = prim.GetStage()->GetEditTarget();
auto layer = editTarget.GetLayer();
Expand Down
10 changes: 5 additions & 5 deletions lib/usd/utils/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ namespace MayaUsdUtils {
MAYA_USD_UTILS_PUBLIC
SdfLayerHandle defPrimSpecLayer(const UsdPrim&);

//! Return a list of layers in strength order that have opinions on a prim
//! Return a list of layers in strength order that have opinions on the argument prim.
MAYA_USD_UTILS_PUBLIC
std::vector<SdfLayerHandle> layersWithOpinion(const UsdPrim&);
std::vector<SdfLayerHandle> layersWithPrimSpec(const UsdPrim&);

//! Check if a layer has any opinions that affects a particular prim
//! Check if a layer has any opinions that affects on the argument prim.
MAYA_USD_UTILS_PUBLIC
bool doesLayerHavePrimSpec(const UsdPrim&);
bool doesEditTargetLayerHavePrimSpec(const UsdPrim&);

//! Return the layer that has any opinions on a particular prim
//! Return the strongest layer that has an opinion on the argument prim.
MAYA_USD_UTILS_PUBLIC
SdfLayerHandle strongestLayerWithPrimSpec(const UsdPrim&);

Expand Down
5 changes: 2 additions & 3 deletions test/lib/ufe/testMatrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ def setUp(self):
# Load plugins
self.assertTrue(self.pluginsLoaded)

# Read in a simple USD scene: a mesh cylinder at the origin.
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test-samples", "cylinder", "usdCylinder.ma" )
cmds.file(filePath, force=True, open=True)
# Open usdCylinder.ma scene in test-samples
mayaUtils.openCylinderScene()

def assertMatrixAlmostEqual(self, ma, mb):
for ra, rb in zip(ma, mb):
Expand Down
7 changes: 2 additions & 5 deletions test/lib/ufe/testObject3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,8 @@ def testBoundingBox(self):
def testAnimatedBoundingBox(self):
'''Test the Object3d bounding box interface for animated geometry.'''

# Load up a scene with a sphere that has an animated radius, with
# time connected to the proxy shape.
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test-samples", "sphereAnimatedRadius", "sphereAnimatedRadiusProxyShape.ma" )

cmds.file(filePath, force=True, open=True)
# Open sphereAnimatedRadiusProxyShape.ma scene in test-samples
mayaUtils.openSphereAnimatedRadiusScene()

# The extents of the sphere are copied from the .usda file.
expected = [
Expand Down
100 changes: 81 additions & 19 deletions test/lib/ufe/testRename.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,49 +98,66 @@ def assertStageAndPrimAccess(
def testRename(self):
'''Rename USD node.'''

# Select a USD object.
ball35Path = ufe.Path([
mayaUtils.createUfePathSegment(
"|world|transform1|proxyShape1"),
usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")])
ball35Item = ufe.Hierarchy.createItem(ball35Path)
ball35Hierarchy = ufe.Hierarchy.hierarchy(ball35Item)
propsItem = ball35Hierarchy.parent()
# open usdCylinder.ma scene in test-samples
mayaUtils.openCylinderScene()

# clear selection to start off
cmds.select(clear=True)

# select a USD object.
mayaPathSegment = mayaUtils.createUfePathSegment('|world|mayaUsdTransform|shape')
usdPathSegment = usdUtils.createUfePathSegment('/pCylinder1')
cylinderPath = ufe.Path([mayaPathSegment, usdPathSegment])
cylinderItem = ufe.Hierarchy.createItem(cylinderPath)
cylinderHierarchy = ufe.Hierarchy.hierarchy(cylinderItem)
propsItem = cylinderHierarchy.parent()

propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)
propsChildrenPre = propsHierarchy.children()

ufe.GlobalSelection.get().append(ball35Item)
ufe.GlobalSelection.get().append(cylinderItem)

# get the USD stage
stage = mayaUsd.ufe.getStage(str(mayaPathSegment))

newName = 'Ball_35_Renamed'
# check GetLayerStack behavior
self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer())
self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetSessionLayer())

# set the edit target to the root layer
stage.SetEditTarget(stage.GetRootLayer())
self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer())

# rename
newName = 'pCylinder1_Renamed'
cmds.rename(newName)

# The renamed item is in the selection.
snIter = iter(ufe.GlobalSelection.get())
ball35RenItem = next(snIter)
ball35RenName = str(ball35RenItem.path().back())
pCylinder1Item = next(snIter)
pCylinder1RenName = str(pCylinder1Item.path().back())

self.assertEqual(ball35RenName, newName)
self.assertEqual(pCylinder1RenName, newName)

# MAYA-92350: should not need to re-bind hierarchy interface objects
# with their item.
propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)
propsChildren = propsHierarchy.children()

self.assertEqual(len(propsChildren), len(propsChildrenPre))
self.assertIn(ball35RenItem, propsChildren)
self.assertIn(pCylinder1Item, propsChildren)

cmds.undo()

def childrenNames(children):
return [str(child.path().back()) for child in children]
return [str(child.path().back()) for child in children]

propsHierarchy = ufe.Hierarchy.hierarchy(propsItem)
propsChildren = propsHierarchy.children()
propsChildrenNames = childrenNames(propsChildren)

self.assertNotIn(ball35RenName, propsChildrenNames)
self.assertIn('Ball_35', propsChildrenNames)
self.assertNotIn(pCylinder1RenName, propsChildrenNames)
self.assertIn('pCylinder1', propsChildrenNames)
self.assertEqual(len(propsChildren), len(propsChildrenPre))

cmds.redo()
Expand All @@ -149,7 +166,52 @@ def childrenNames(children):
propsChildren = propsHierarchy.children()
propsChildrenNames = childrenNames(propsChildren)

self.assertIn(ball35RenName, propsChildrenNames)
self.assertNotIn('Ball_35', propsChildrenNames)
self.assertIn(pCylinder1RenName, propsChildrenNames)
self.assertNotIn('pCylinder1', propsChildrenNames)
self.assertEqual(len(propsChildren), len(propsChildrenPre))
ppt-adsk marked this conversation as resolved.
Show resolved Hide resolved

def testRenameRestriction(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about "testRenameRestrictionSameLayerDef", or similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ppt-adsk Merci Pierre. please see 581932b

'''Restrict renaming USD node. Cannot rename a prim defined on another layer.'''

# select a USD object.
mayaPathSegment = mayaUtils.createUfePathSegment('|world|transform1|proxyShape1')
usdPathSegment = usdUtils.createUfePathSegment('/Room_set/Props/Ball_35')
ball35Path = ufe.Path([mayaPathSegment, usdPathSegment])
ball35Item = ufe.Hierarchy.createItem(ball35Path)

ufe.GlobalSelection.get().append(ball35Item)

# get the USD stage
stage = mayaUsd.ufe.getStage(str(mayaPathSegment))

# check GetLayerStack behavior
self.assertEqual(stage.GetLayerStack()[0], stage.GetSessionLayer())
self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetSessionLayer())

# expect the exception happens
with self.assertRaises(RuntimeError):
newName = 'Ball_35_Renamed'
cmds.rename(newName)

def testRenameRestriction2(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about "testRenameRestrictionOtherLayerOpinions", or similar?

'''Restrict renaming USD node. Cannot rename a prim with definitions or opinions on other layers.'''

# select a USD object.
mayaPathSegment = mayaUtils.createUfePathSegment('|world|transform1|proxyShape1')
usdPathSegment = usdUtils.createUfePathSegment('/Room_set/Props/Ball_35')
ball35Path = ufe.Path([mayaPathSegment, usdPathSegment])
ball35Item = ufe.Hierarchy.createItem(ball35Path)

ufe.GlobalSelection.get().append(ball35Item)

# get the USD stage
stage = mayaUsd.ufe.getStage(str(mayaPathSegment))

# set the edit target to Assembly_room_set.usda
stage.SetEditTarget(stage.GetLayerStack()[2])
self.assertEqual(stage.GetEditTarget().GetLayer().GetDisplayName(), "Assembly_room_set.usda")

# expect the exception happens
with self.assertRaises(RuntimeError):
newName = 'Ball_35_Renamed'
cmds.rename(newName)
Copy link
Contributor Author

@HamedSabri-adsk HamedSabri-adsk May 5, 2020

Choose a reason for hiding this comment

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

@ppt-adsk I added two test cases for rename restriction operations:

testRenameRestriction is to test if the prim that we are about to rename is in the correct layer

testRenameRestriction2 is to test if the prim that we are about to rename doesn't have any definitions or opinions on other layers.

If you think testRenameRestriction2 is ugly, I am happy to change it with something more meaningful.

8 changes: 2 additions & 6 deletions test/lib/ufe/testRotatePivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,8 @@ def setUp(self):
# Load plugins
self.assertTrue(self.pluginsLoaded)

# Create a simple USD scene. Ideally this would be done
# programmatically, but as of 28-Jun-2018 attempts to do so have
# resulted in a file that crashes Maya on pickWalk of the scene.
# Use a saved scene read from file instead.
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test-samples", "twoSpheres", "twoSpheres.ma" )
cmds.file(filePath, force=True, open=True)
# Open twoSpheres.ma scene in test-samples
mayaUtils.openTwoSpheresScene()

def testRotatePivot(self):
# mayaSphere is at (10, 0, 10) in local space, and since it has no
Expand Down
13 changes: 12 additions & 1 deletion test/lib/ufe/ufeTestUtils/mayaUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,15 @@ def openTopLayerScene():
# Open top_layer file which contains the USD scene
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "test-samples", "ballset", "StandaloneScene", "top_layer.ma" )
cmds.file(filePath, force=True, open=True)


def openCylinderScene():
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "test-samples", "cylinder", "usdCylinder.ma" )
cmds.file(filePath, force=True, open=True)

def openTwoSpheresScene():
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "test-samples", "twoSpheres", "twoSpheres.ma" )
cmds.file(filePath, force=True, open=True)

def openSphereAnimatedRadiusScene():
filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "test-samples", "sphereAnimatedRadius", "sphereAnimatedRadiusProxyShape.ma" )
cmds.file(filePath, force=True, open=True)
ppt-adsk marked this conversation as resolved.
Show resolved Hide resolved