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

Conversation

HamedSabri-adsk
Copy link
Contributor

@HamedSabri-adsk HamedSabri-adsk commented Apr 29, 2020

PR Changes:

This PR brings new improvements to rename command and adds new restrictions:

  • Reworked rename command implementation.
  • Only allow edits on the target layer. error out if the object is not defined on the target layer.
  • Error out if object to rename has any definitions or overs on any stronger or weaker layers. A Reference to a weaker layer should be nameable if it meets all other criteria here.
  • Error out if the object to be renamed has been referenced by another layer. Internal references (references without a file path specified) from the same file, should be nameable.
  • Created utilities for common USD operations under lib\usd\utils\util.h

Unit tests:

  • Previous testRename is renamed to testRenameUndo.
  • Added 3 additional tests for newly added restriction logic.
testRename
testRenameUndo
testRenameRestrictionSameLayerDef
testRenameRestrictionOtherLayerOpinions
testRenameRestrictionExternalReference

…arget layer errors out:

Unable to rename [object name] on current target layer, please set [Layername] as target layer to proceed.
prim.GetName().GetString(),
possibleTargetLayer->GetDisplayName());
throw std::runtime_error(err.c_str());
}
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.

UsdSceneItem::Ptr m_fUfeSrcItem;
SdfPath m_fUsdSrcPath;
UsdSceneItem::Ptr m_fUfeDstItem;
SdfPath m_fUsdDstPath;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Following the C++ guideline, member variables need to be prefixed with m_

Choose a reason for hiding this comment

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

Actually, we use _ prefix - see https://github.com/Autodesk/maya-usd/blob/dev/doc/codingGuidelines.md#naming-file-type-variable-constant-function-namespace-macro-template-parameters-schema-names

New modules should start using these guidelines, but we still have to plan how to adapt the existing code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My bad, I was sill thinking about "m_" :) Fixed in 54166d2

@@ -109,8 +109,6 @@ SdfLayerHandle defPrimSpecLayer(const UsdPrim& prim)

SdfLayerHandle defLayer;
auto layerStack = prim.GetStage()->GetLayerStack();
auto stage = prim.GetStage();
auto primFromPath = stage->GetPrimAtPath(prim.GetPath());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove unused variables.

}

return true;
}
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.

To find out if a specific layer contains any opinions that affect a particular prim, we can use the Sdf's GetPrimAtPath() for that prim. If the primSpec result returns nothing that means the target layer doesn't contain any opinions on a particular prim.

}
return targetLayer;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is used to give the user a hint to what target layer to set in order to rename a particular prim.

@HamedSabri-adsk HamedSabri-adsk added core Related to core library ufe-usd Related to UFE-USD plugin in Maya-Usd unit test Related to unit tests (both python or c++) labels Apr 29, 2020
@@ -124,6 +122,38 @@ SdfLayerHandle defPrimSpecLayer(const UsdPrim& prim)
return defLayer;
}

bool isTargetLayerHaveOpinion(const UsdPrim& prim)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would read better as
doesTargetLayerHaveOpinion()
or just plain
targetLayerHasOpinion()
(shorter is often better).


// to know whether the target layer contains any opinions that
// affect a particular prim, there must be a primSpec for that prim
if (!primSpec) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit-pick: if SdfPrimSpec supports an
explicit operator bool()
(looks like it does, but couldn't find it)
could be
return bool(primSpec);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point @ppt-adsk but I don't find any "explicit operator bool" both in SdfSpec and SdfPrimSpec

return true;
}

SdfLayerHandle targetLayerWithOpion(const UsdPrim& prim)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Typo: "Opion" --> "Opinion".
Also, isn't it the case that this will return the highest-priority layer that has an opinion, either def or over? It would be good if the function would say that, e.g.
SdfLayerHandle strongestLayerWithOpinion(const UsdPrim& prim)
or
SdfLayerHandle strongestLayerWithPrimSpec(const UsdPrim& prim)

A minor point is that the existing function in the Utils file is called defPrimSpecLayer, i.e. uses "prim spec" rather than "opinion". Perhaps we should standardize on one terminology or the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see 54166d2

I agree with standardization on one terminology, I picked "prim spec" instead of "opinion" for now.
Regarding the function names, I made following changes and dropped the target. Let me know if this reads better:

Before:

isTargetLayerHaveOpinion 
targetLayerWithOpion      

After:

doesLayerHavePrimSpec  
strongestLayerWithPrimSpec

lib/mayaUsd/ufe/Utils.h Show resolved Hide resolved
@@ -56,6 +56,14 @@ bool isRootChild(const Ufe::Path& path);
MAYAUSD_CORE_PUBLIC
SdfLayerHandle defPrimSpecLayer(const UsdPrim& prim);

//! Check if the target layer has any opinions that affects a particular prim
MAYAUSD_CORE_PUBLIC
bool isTargetLayerHaveOpinion(const UsdPrim& prim);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel the naming of these two functions is confusing, because they both talk about "target layer", but they're not related.
"isTargetLayerHaveOpinion" is really answering the question: does the edit target layer on the prim's stage have an opinion?
"targetLayerWithOpion" [sic] is answering the question: what is the strongest layer that has an opinion for this prim? From the point of view of this function, there is no target involved, and therefore I would remove the word "target" here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see 54166d2

I agree. I made following changes and dropped the target. Let me know if this reads better:

Before:

isTargetLayerHaveOpinion 

After:

doesLayerHavePrimSpec  

"doesLayerHavePrimSpec" answers this question: Does the layer that you are currently on have any primSpec (a.k.a opinion) on the selected prim?

std::string err = TfStringPrintf("No prim found at %s", prim.GetPath().GetString().c_str());
throw std::runtime_error(err.c_str());
}

// check if the target layer has any opinions that affects selected prim
if (!isTargetLayerHaveOpinion(prim)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

So here if the edit target doesn't have an opinion, we tell the user to set the edit target to the strongest layer than does have an opinion. O.K., maybe that makes sense, even if the strongest layer only has "over" opinion.

But what if the edit target is on a weak layer and the weak layer DOES have an opinion, that is overridden by an opinion in a stronger layer? We're saying that's O.K.? The user will rename in the weak layer, but the stronger layer will still have its opinion and it will be composed into the final result, no? That doesn't seem to meet Will's acceptance criteria that "Trying to rename an object with opinions on stronger layers error out." Perhaps what you're showing here is really work in progress and you're going to fix this? Or is there something I haven't understood?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very valid question and I should have elaborated more on the change here:

if (!MayaUsdUtils::doesLayerHavePrimSpec(prim)) solves this request:

Trying to rename an object that was not defined on the target layer errors out

but as you also mentioned it doesn't really solve this request:

Trying to rename an object with opinions on stronger layers error out.

I am working on addressing this as we speak.

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 please see 8f8295f.

- renamed usd utility functions related to "usd" and "sdf" operations
- moved usd utility functions from mayaUsd\ufe\Utils.h to usd\utils\util.h under MayaUsdUtils namespace.
- cleaned up variable names according to c++ guideline.
}

SdfLayerHandle
MayaUsdUtils::strongestLayerWithPrimSpec(const UsdPrim& prim)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

extra MayaUsdUtils:: in line 64 and 48 was added by accident. I will fix this in the next PR.

Hamed Sabri added 2 commits May 1, 2020 15:45
Error if object to rename has ANY definitions or overs on ANY stronger or weaker layers. This is the most restrictive we can be. We will revisit how much we loosen this up later.

Cannot rename a prim with definitions or opinions on other layers. Opinions exist in [layer name 1], [layer name 2], [layer name 3], [layer name 4],etc...”
}

std::vector<SdfLayerHandle>
layersWithOpinion(const UsdPrim& prim)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Naming: the other functions in this translation units use primSpec, this one should as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

please see 4c67b69

// ordered from strongest to weakest opinion.
const auto& primStack = prim.GetPrimStack();

std::vector<SdfLayerHandle> layersWithOpion;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Typo: "layersWithOpion" --> "layersWithOpinion", but of course if you rename to primSpec this will disappear.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see 4c67b69

Some how I can't type opinion properly :)

}

bool
doesLayerHavePrimSpec(const UsdPrim& prim)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the naming would be much clearer as
doesEditTargetLayerHavePrimSpec()
I know it's wordy, maybe you can do better, but this name (or a similar name) tells you which layer we're talking about, the edit target layer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I like your suggested name.

see 4c67b69

MAYA_USD_UTILS_PUBLIC
bool doesLayerHavePrimSpec(const UsdPrim&);

//! Return the layer that has any opinions on a particular prim
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be clearer as
"Return the strongest layer that has an opinion on the argument prim."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see 4c67b69

_ufeSrcItem = srcItem;
_usdSrcPath = prim.GetPath();

// Every call to rename() (through execute(), undo() or redo()) removes
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit-pick: indentation changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see 4c67b69

std::string err = TfStringPrintf("No prim found at %s", prim.GetPath().GetString().c_str());
throw std::runtime_error(err.c_str());
}

// if the current layer doesn't have any opinions that affects selected prim
if (!MayaUsdUtils::doesLayerHavePrimSpec(prim)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this just _layer != theEditTargetLayer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Correct, we could also use _layer != prim.GetStage()->GetEditTarget().GetLayer() instead. I keep using doesEditTargetLayerHavePrimSpec which is a bit more generic. Hope this is Ok.

for (auto layer : layers) {
layerNames.append("[" + layer->GetDisplayName() + "]" + ",");
}
layerNames.pop_back();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why omit the last layer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see 4c67b69

I rename layerNames to layerDisplayNames so it reads better.

I am not omitting any layers and rather the extra "," at the end of layerDisplayNames string.

This is the message the design wants to show up as part of the error message:

Opinions exist in [layer name 1], [layer name 2], [layer name 3], [layer name 4].”

So when I iterate through the layers, I get their GetDisplayName and concatenate them in the
layerDisplayNames string separated by ","

for (auto layer : layers) {
    layerDisplayNames.append("[" + layer->GetDisplayName() + "]" + ",");
}
layerDisplayNames.pop_back();

There are better ways of doing this, but this is one way I did.

Hamed Sabri added 2 commits May 4, 2020 11:28
- edited code logics in testRename to use usdCylinder.ma scene since with new rename restrictions top_layer.ma doesn't meet the requirements for testing undo/redo
- refactored code around opening maya test scenes and introduced three utility functions (openCylinderScene, openTwoSpheresScene, openSphereAnimatedRadiusScene)
# 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.

Copy link
Collaborator

@ppt-adsk ppt-adsk left a comment

Choose a reason for hiding this comment

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

Almost there! Minor tweak to just rename the test functions, and we're all done.

self.assertEqual(len(propsChildren), len(propsChildrenPre))

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

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?

@ppt-adsk
Copy link
Collaborator

ppt-adsk commented May 5, 2020

Great job Hamed!

tbb\concurrent_vector.h(177): fatal error C1017: invalid integer constant expression

Ufe::ObjectRename notification(_ufeDstItem, ufeSrcPath);
Ufe::Scene::notifyObjectPathChange(notification);
sendRenameNotification(_ufeDstItem, _ufeSrcItem->path());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

minor refactoring here.

{
UsdEditContext ctx(_stage, _layer);
status = _stage->RemovePrim(_ufeSrcItem->prim().GetPath());
}
Copy link
Contributor Author

@HamedSabri-adsk HamedSabri-adsk May 6, 2020

Choose a reason for hiding this comment

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

With the new restriction in place it is now safe to remove the prim from the scene graph rather than deactivating it.When RemovePrim is called, I simply pass the _ufeSrcItem->prim().GetPath() rather than creating a new srcPrim which would be really unnecessary.

Redo operation:
1- Call SdfCopySpec to copy ALL the child specs from _usdSrcPath to _usdDstPath. This is a crucial call.
2- Remove the prim at _ufeSrcItem USD path.
3- set _ufeDstItem by calling createSiblingSceneItem
4- send rename notification

UsdSceneItem::Ptr _ufeDstItem;
SdfPath _usdDstPath;
UsdSceneItem::Ptr _ufeDstItem;
SdfPath _usdDstPath;

}; // UsdUndoRenameCommand

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have converted tabs to dots. If you find them distracting, Github allow hiding whitespace changes during review:

Please see
https://medium.com/@kevinpmcc/hide-whitespace-for-easier-github-code-reviews-d3f476d03863

@HamedSabri-adsk
Copy link
Contributor Author

@ppt-adsk I have the first stab at removing the cruft in USD files as objects are named and renamed. please see 20be9ac

@HamedSabri-adsk
Copy link
Contributor Author

@ppt-adsk Ah....... I am starting to think why we need to rely on SdfCopySpec any more.

I honestly think we can simply rename a prim in place. I just realized that SdfPrimSpec has a routine called SetName to set the prim name:

https://graphics.pixar.com/usd/docs/api/class_sdf_prim_spec.html#aa1f241d28f26488867a276eca6d54405

This would really simplify a lot of things. Stay tuned.....

This change simplifies the rename operation by simply renaming a prim in place via SdfPrimSpec's SetName routine and removes the old work-flow involving SdfCopySpec.
@HamedSabri-adsk HamedSabri-adsk marked this pull request as ready for review May 6, 2020 17:03
Hamed Sabri and others added 16 commits May 6, 2020 17:30
…operations in undo/redo.

- To read the code better, I added a new member variable _oldName that holds the prim's original name.
…sd file for testing purposes.

- Renamed testRename to testRenameUndo
- Added new logics for testing new way of renaming a prim name via SdfPrimSpec.
…s around the stage behavior that deserves an explanation.
- Add a simple testRenameRestrictionExternalReference unit test.
…ternal reference and create a new utility for it.
… we do still want the opinion detection in other layers happens.
…things more readable.

- change the container type for layersWithContribution method from std::vector to std::set to guarantee having unique layers.
// SdfLayer is a "simple" container, and all it knows about defaultPrim is that it is a piece of token-valued layer metadata.
// It is only the higher-level Usd and Pcp modules that know that it is identifying a prim on the stage.
// One must use the SdfLayer API for setting the defaultPrim when you rename the prim it identifies.
if(primNameStr == stageDefPrimNameStr){
Copy link
Contributor Author

@HamedSabri-adsk HamedSabri-adsk May 14, 2020

Choose a reason for hiding this comment

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

@ppt-adsk We don't need to do a comparison here. if(!stageDefPrimNameStr.empty()) does the work. Will fix it in the next commit,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I don't think that would work now that I think about it. please ignore my last comment.

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 please see d84d82f

Copy link
Collaborator

@ppt-adsk ppt-adsk left a comment

Choose a reason for hiding this comment

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

Just that minor out of date comment to remove, but otherwise looks good to me!

// MAYA-92264: Pixar bug prevents undo from working. Try again with USD
// version 0.8.5 or later. PPT, 7-Jul-2018.
try {
// MAYA-92264: Pixar bug prevents undo from working. Try again with USD
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is no longer valid, right?

@kxl-adsk kxl-adsk merged commit 2ae67f5 into dev May 20, 2020
@kxl-adsk kxl-adsk deleted the sabrih/MAYA-104215/improve_usd_rename_operation branch May 20, 2020 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Related to core library ufe-usd Related to UFE-USD plugin in Maya-Usd unit test Related to unit tests (both python or c++)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants