-
Notifications
You must be signed in to change notification settings - Fork 202
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-106755: Support automatic updating of internal references when the path they are referencing has changed. #797
MAYA-106755: Support automatic updating of internal references when the path they are referencing has changed. #797
Conversation
…eferences if the path they are referencing has changed.
TfToken::HashSet childrenNames; | ||
for (auto child : prim.GetParent().GetChildren()){ | ||
childrenNames.insert(child.GetName()); | ||
} | ||
if (childrenNames.find(TfToken(_newName)) != childrenNames.end()){ | ||
_newName = uniqueName(childrenNames, _newName); | ||
} | ||
|
||
// names are not allowed to start to digit numbers | ||
if(std::isdigit(_newName.at(0))){ | ||
_newName = prim.GetName(); | ||
} | ||
|
||
// all special characters are replaced with `_` | ||
const std::string specialChars{"~!@#$%^&*()-=+,.?`':{}|<>[]/ "}; | ||
std::replace_if(_newName.begin(), _newName.end(), [&](auto c){ | ||
return std::string::npos != specialChars.find(c); | ||
}, '_'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved all the code for making the new name unique to the constructor. This is for sake of making the renameRedo clean and also there is no real benefit to keep calling them on every redo operation.
for (const auto& p : newPrim.GetStage()->TraverseAll()) { | ||
|
||
// check is this prim has a spec | ||
SdfChangeBlock changeBlock; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added SdfChangeBlock to make the loop more performant and delay sending notification until after we exist this function.
Based on what we discussed internally the other day, having SdfChangeBlock on the stack while making calls into the Usd API's could be troublesome but I didn't see any issue in this case.
lib/usd/utils/util.cpp
Outdated
// add the new internal reference | ||
status = p.GetReferences().AddInternalReference(newPath); | ||
status = p.GetReferences().AddInternalReference(finalPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently there is no APIs either at Sdf or Usd level to help querying the "append" or "prepend" operations for the entries in the list. Therefore, UsdListPosition is always set by the default value UsdListPositionBackOfPrependList passed to AddInternalReference.
This needs to be documented.
return false; | ||
} | ||
// send notification to update UFE data model | ||
sendNotification<Ufe::ObjectRename>(_ufeDstItem, _ufeSrcItem->path()); | ||
|
||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
Updating the internal references paths should happen first before the primSpec->SetName.
-
Tidy up renameRedo a little bit and moved the unique name logics to the constructor.
…suggestion from Pixar.
itemVector.Replace(ref, newRef); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ppt-adsk Per conversation with Spiff, it was suggested that there would be a more efficient technique to approach how the internal references list ops can be updated.
In the new approach we directly replacing the old SdfReference with the new SdfReference and call the itemVector Replace() to switch them. This is more efficient than using the Usd API approach where I needed to clear the list first and add the new one reference.
|
||
// update append/prepend lists individually | ||
replaceReferenceItems(oldPrim, newPath, referencesList, SdfListOpTypeAppended); | ||
replaceReferenceItems(oldPrim, newPath, referencesList, SdfListOpTypePrepended); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ppt-adsk We need to update each individual list separately. For now, I am only concerned about append and prepend list Ops.
As a side note, the word "list" could be a bit confusing. Each list edit operation ( e.g appen , prepend, etc.. ) are stored in std::vector like containers.
An SdfListProxy represents a single list of list editing operations, making it look like an STL vector (modeling a random access container and back insertion sequence).
https://github.com/PixarAnimationStudios/USD/blob/release/pxr/usd/sdf/listProxy.h
std::replace_if(_newName.begin(), _newName.end(), [&](auto c){ | ||
return std::string::npos != specialChars.find(c); | ||
}, '_'); | ||
auto newSceneItem = createSiblingSceneItem(_ufeSrcItem->path(), _newName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing the string manipulation, can't we just dynamic_cast to a UsdSceneItem and get the new prim's path, much like on (new) line 117?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @ppt-adsk let's have a chat offline tomorrow. I am not sure if the dynamic_cast would work here but maybe I am missing something obvious.
status = primSpec->SetName(_newName); | ||
if (!status) { | ||
return false; | ||
} | ||
} | ||
|
||
// the renamed scene item is a "sibling" of its original name. | ||
_ufeDstItem = createSiblingSceneItem(_ufeSrcItem->path(), _newName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a duplicate of line 119.
return false; | ||
auto newSceneItem = createSiblingSceneItem(_ufeSrcItem->path(), _ufeSrcItem->prim().GetName()); | ||
auto newUfePath = Ufe::Path(newSceneItem->path().getSegments()[1]); | ||
bool status = MayaUsdUtils::updateInternalReferencesPath(prim, SdfPath(newUfePath.string())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as redo(). In fact, undo() and redo() are so similar it would be good to factor out the code into a separate private method and call that from undo() and redo() with the proper arguments.
} | ||
|
||
// shouldn't have to again create a sibling sceneItem here since we already have a valid _ufeSrcItem | ||
// however, I get random crashes if I don't which needs furthur investigation. HS, 6-May-2020. | ||
// the renamed scene item is a "sibling" of its original name. | ||
_ufeSrcItem = createSiblingSceneItem(_ufeDstItem->path(), _ufeSrcItem->prim().GetName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as redo, duplicate of line 161.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, the duplicate can be cleaned up for sure.
lib/usd/utils/util.cpp
Outdated
SdfListOpType op) | ||
{ | ||
// set the itemVector based on the SdfListOpType | ||
auto itemVector = referencesList.GetAppendedItems(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awkward that we assign to the itemVector unconditionally, then potentially squash it at lines 82-89.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
itemVector is of type SdfListProxyand can't be created without initializing.
error C2512: 'pxrInternal_v0_20__pxrReserved__::SdfListProxy<pxrInternal_v0_20__pxrReserved__::SdfReferenceTypePolicy>': no appropriate default constructor available
This was what I originally had in mind.
// set the itemVector based on the SdfListOpType
SdfReferencesProxy::ListProxy itemVector;
if (op == SdfListOpTypeAppended) {
itemVector = referencesList.GetAppendedItems();
} if (op == SdfListOpTypePrepended) {
itemVector = referencesList.GetPrependedItems();
} else if (op == SdfListOpTypeOrdered) {
itemVector = referencesList.GetOrderedItems();
} else if (op == SdfListOpTypeAdded) {
itemVector = referencesList.GetAddedItems();
} else if (op == SdfListOpTypeDeleted) {
itemVector = referencesList.GetDeletedItems();
}
I also went with auto to be on the safe side but the type here is SdfListProxy<SdfReferenceTypePolicy>
or a.k.a ( SdfReferencesProxy::ListProxy )
std::cout << "type = " << typeid(itemVector).name() << '\n';
outputs: SdfListProxy<SdfReferenceTypePolicy>
Also the name "itemVector" is a bit misleading which is the return type for GetAppendedItems, GetPrependedItems, GetOrderedItems, GetAddedItems, GetDeletedItems in SdfListOp but what we have here is from SdfListEditorProxy. There is no Replace() in SdfListOp. I addressed this in 0618a8e to make it more clear.
See SdfListOp vs SdfListEditorProxy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O.K., you want ugly, I can give you ugly :)
SdfReferencesProxy::ListProxy itemVector = (op == SdfListOpTypeAppended) ? referencesList.GetAppendedItems() : (op == SdfListOpTypePrepended) ? referencesList.GetPrependedItems() : (op == SdfListOpTypeOrdered) ? referencesList.GetOrderedItems() : (op == SdfListOpTypeAdded) ? referencesList.GetAddedItems() : (op == SdfListOpTypeDeleted) ? referencesList.GetDeletedItems() : SdfReferencesProxy::ListProxy();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our offline conversation, I will leave things as is to avoid making broader changes.
auto newUfePath = Ufe::Path(newSceneItem->path().getSegments()[1]); | ||
bool status = MayaUsdUtils::updateInternalReferencesPath(prim, SdfPath(newUfePath.string())); | ||
auto ufeSiblingPath = _ufeSrcItem->path().sibling(Ufe::PathComponent(_newName)); | ||
bool status = MayaUsdUtils::updateInternalReferencesPath(prim, SdfPath(ufeSiblingPath.getSegments()[1].string())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ppt-adsk Thank you for pointing out to use sibling().
…ashes maya * Use the uniqueChildName() for the undo rename command.
@HamedSabri-adsk @seando-adsk Now that PF is back online, I re-run the check and Maya 2020 fails. Please have a look. For now, I'm removing |
Thanks @kxl-adsk . It looks like a UFE1.0 issue after @seando-adsk change in my branch.
|
@HamedSabri-adsk Darn UFE v1 support. I'll have a look. |
…ashes maya * Fix to support UFE v1 (Maya 2020 and earlier).
@HamedSabri-adsk / @kxl-adsk I pushed a fix and restarted the Preflight. |
…ave 'IsInternal' routine.
#else | ||
return ref.GetAssetPath().empty(); | ||
#endif | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsInternal() is a new routine that I added to SdfReference as part of a PR and is supported as of 20.08
.
Added a guard to make sure I am not breaking older USD version ( e.g 19.11 )
} | ||
|
||
return isInternalRef; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This piece of code didn't make much sense and it is not used anymore, therefore removing it.
Description
The goal of this PR is to allow clients to be able to rename prims that are internally referenced by other prims and not worry about breaking them. The same workflow can also later be applied to other composition arcs ( e.g inherits or specializes ).