-
Notifications
You must be signed in to change notification settings - Fork 203
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-104214 - As a user, I'd like to add a USD prim to an existing stage #489
MAYA-104214 - As a user, I'd like to add a USD prim to an existing stage #489
Conversation
@@ -8,7 +8,7 @@ if (CMAKE_UFE_V2_FEATURES_AVAILABLE) | |||
install(FILES __init__.py DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/python/ufe_ae/usd/nodes) | |||
|
|||
# Maya Attribute Editor python template files | |||
foreach(_SUBDIR base camera material mesh scope shader xform) | |||
foreach(_SUBDIR base camera capsule cone cube cylinder material mesh scope shader sphere xform) |
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.
Created AE templates for the missing prim types that are part of the menu. They are all just copies of the existing templates that have everything in the "AEBaseTemplate"
g_MayaContextOpsHandler = Ufe::RunTimeMgr::instance().contextOpsHandler(g_MayaRtid); | ||
auto proxyShapeContextOpsHandler = ProxyShapeContextOpsHandler::create(g_MayaContextOpsHandler); | ||
Ufe::RunTimeMgr::instance().setContextOpsHandler(g_MayaRtid, proxyShapeContextOpsHandler); |
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.
Decorate the Maya context ops handler, just like we do with the Maya hier handler. Note: currently the Maya context ops handler is not used so it's null.
auto usdItem = UsdSceneItem::create(item->path(), stage->GetPseudoRoot()); | ||
auto usdContextOpsHandler = UsdContextOpsHandler::create(); | ||
auto cOps = usdContextOpsHandler->contextOps(usdItem); | ||
UsdContextOps::Ptr usdCOps = std::dynamic_pointer_cast<UsdContextOps>(cOps); |
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.
From Maya the input Ufe::SceneItem will be of an internal type to Maya. We need to create a UsdSceneItem (that has a prim).
#if UFE_PREVIEW_VERSION_NUM >= 2015 | ||
else if (!prim.IsValid()) | ||
{ | ||
auto notification = Ufe::ObjectDestroyed(ufePath); | ||
Ufe::Scene::notifyObjectDelete(notification); | ||
} | ||
#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.
New scene notification added in Ufe. When this message is sent the object is no longer valid. Thus we don't use the scene item, but instead just the path.
void redo() override | ||
{ | ||
if (_stage) { | ||
auto newPrim = _stage->DefinePrim(_primPath, _primToken); |
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.
Create a prim with the specified path and of the specified type.
std::string("Make Visible") : std::string("Make Invisible"); | ||
items.emplace_back("Toggle Visibility", l); | ||
// Top-level items. Variant sets and visibility. Do not add for gateway type node. | ||
if (!fIsAGatewayType) { |
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.
Lines 159-174 were indented inside this new if test. The gateway node will be the mayausd proxy shape.
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.
As I discussed with you, I'm still trying to figure out how we could have done this with a base class, and avoided the type-based conditional...
If we had a base class for the context ops that did not have variant sets and visibility, and a derived class that would have variant sets and visibility first, then would call the base class, would that work? Ordering would be preserved.
Maybe I'm too attached to that idea, and maybe it's just a minor improvement, but my feeling is that it would help with separation of concerns. The base class would take care of the proxy shape-only context ops, and the derived class would add in visibility and variant sets. Let me know what you think.
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.
It would work for now only because in our ordering we have the two non-gateway node stuff first and then a single item that applies to all. But as we add more context menu items this won't work. Example: my next task is to add menu item for "Layer Editor" as the first menu item. It will be added for all types. Once we add that I think the complexity of using a base/derived class outweighs the benefit.
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 with you, the base class approach isn't flexible enough to accomodate the ordering we'll need. Case closed.
"Variant Sets", "Variant Sets", Ufe::ContextItem::kHasChildren); | ||
} | ||
auto attributes = Ufe::Attributes::attributes(sceneItem()); | ||
if (attributes && attributes->hasAttribute(UsdGeomTokens->visibility)) { |
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 the hasAttribute() test here to fix a bug with prims that don't have a visibility attribute to toggle.
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.
Thanks. For my own education, which ones don't? The typeless defs?
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.
Typeless = yes, but also shaders, materials
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.
Ah, of course. Can you add that in as a code comment, to avoid further questions?
lib/mayaUsd/ufe/UsdContextOps.cpp
Outdated
if (itemPath[1] == "Def") | ||
_primToken = TfToken(); // create typeless prim | ||
else | ||
_primToken = TfToken(itemPath[1]); |
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.
Type of prim to create. Def is typeless.
* Added new AE templates for basic prim types. * New ProxyShapeContextOpsHandler to decorate the one from Maya. * Send new Ufe::ObjectDestroyed notif when USD prim is deleted. * New context item sub-menu: "Add New Prim" with various prim types. Full undo/redo support thur new command. Added new context ops test "testAddNewPrim". * Fixed a bug where "visbility" context menu item was added to prims that don't support visibility attribute.
cb4eddb
to
ffc9e5c
Compare
public: | ||
typedef std::shared_ptr<ProxyShapeContextOpsHandler> Ptr; | ||
|
||
ProxyShapeContextOpsHandler(Ufe::ContextOpsHandler::Ptr mayaContextOpsHandler); |
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.
Nit-pick: should pass in the shared_ptr by const ref.
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.
Agreed. I had originally copied this from the ProxyShapeHierarchyHandler. It wasn't const ref either so I changed it.
ProxyShapeContextOpsHandler& operator=(ProxyShapeContextOpsHandler&&) = delete; | ||
|
||
//! Create a ProxyShapeContextOpsHandler from a UFE hierarchy handler. | ||
static ProxyShapeContextOpsHandler::Ptr create(Ufe::ContextOpsHandler::Ptr mayaContextOpsHandler); |
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.
Pass by const ref.
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.
Agreed - same as above.
_stage = MayaUsd::ufe::getStage(Ufe::Path(dagSegment)); | ||
if (_stage) { | ||
// Rename the new prim for uniqueness, if needed. | ||
Ufe::Path newUfePath = ufePath + itemPath[1]; |
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.
You've tried this, so clearly it works, but I'm just not sure how :) In the parenting code, when I parent just below the proxy shape, I needed to take care of the fact that the newly-added component is in a different run-time:
https://github.com/Autodesk/maya-usd/blob/dev/lib/mayaUsd/ufe/UsdUndoInsertChildCommand.cpp#L53
Why don't you have the same problem? If you just concatenate the prim name to the existing proxy shape UFE path, you'll get a path that is in the Maya run-time, which is incorrect.
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.
You are right, I didn't think about this. But I just tested it again and it works. The reason is that in "uniqueChildName" it just does:
std::string childName = childPath.back().string();
which in my case is fine, because it will just return the "itemPath[1]" that I appended. Then it loops thru all the existing child names to see if any match.
Do you think it is okay like this? Alternatively I could pass in (to the command) if I have a gateway node. When I do, I would know that the new prim name I need to append will be in a different runtime and I could handle that in a manner similar to what you did.
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 think it's O.K. like this, but I'd add in a comment somehow to say something like "Hey I know this isn't 100% correct, but all uniqueName does is take the back() of the path, so it's fine."
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.
Done. I also added another code comment for the visibility.
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.
Thanks.
{ | ||
public: | ||
AddNewPrimUndoableCommand(const MayaUsd::ufe::UsdSceneItem::Ptr& usdSceneItem, | ||
const Ufe::ContextOps::ItemPath& itemPath) |
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.
Looks like you're only using itemPath[1]. If that's the case, couldn't we just pass that in, and give it a more readable name, i.e. const std::string& primType, or something similar?
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.
Yes I am, but this way it more matches the other undoable command which also takes in the itemPath.
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.
lib/mayaUsd/ufe/UsdContextOps.cpp
Outdated
|
||
// The type of prim we were asked to create. | ||
if (itemPath[1] == "Def") | ||
_primToken = TfToken(); // create typeless prim |
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.
Nit-pick: could be in the constructor's initializer list as
_primToken(itemPath[1] == "Def" ? TfToken() : TfToken(itemPath[1]))
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'll use the single line version, but I prefer to keep it here rather that in init list because I have some comments.
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.
std::string("Make Visible") : std::string("Make Invisible"); | ||
items.emplace_back("Toggle Visibility", l); | ||
// Top-level items. Variant sets and visibility. Do not add for gateway type node. | ||
if (!fIsAGatewayType) { |
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.
As I discussed with you, I'm still trying to figure out how we could have done this with a base class, and avoided the type-based conditional...
If we had a base class for the context ops that did not have variant sets and visibility, and a derived class that would have variant sets and visibility first, then would call the base class, would that work? Ordering would be preserved.
Maybe I'm too attached to that idea, and maybe it's just a minor improvement, but my feeling is that it would help with separation of concerns. The base class would take care of the proxy shape-only context ops, and the derived class would add in visibility and variant sets. Let me know what you think.
"Variant Sets", "Variant Sets", Ufe::ContextItem::kHasChildren); | ||
} | ||
auto attributes = Ufe::Attributes::attributes(sceneItem()); | ||
if (attributes && attributes->hasAttribute(UsdGeomTokens->visibility)) { |
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.
Thanks. For my own education, which ones don't? The typeless defs?
|
||
// At this point we know we have 2 arguments to execute the operation. | ||
// itemPath[1] contains the new prim type to create. | ||
return std::make_shared<AddNewPrimUndoableCommand>(fItem, itemPath); |
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.
As per above I'd just pass in itemPath[1] 'cause that's all you need.
* Code review comments.
* Added a couple of code comments.
_stage = MayaUsd::ufe::getStage(Ufe::Path(dagSegment)); | ||
if (_stage) { | ||
// Rename the new prim for uniqueness, if needed. | ||
Ufe::Path newUfePath = ufePath + itemPath[1]; |
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.
Thanks.
* Fix problem with new Ufe::ObjectDestroyed() and invalid prim.
I discovered a problem while doing some more local testing. We want to ignore resycn paths that prim paths. See commit 17c9a05 |
if (!changedPath.IsPrimPath()) | ||
continue; |
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.
At line 221 we specifically ask for the prim at path. So if this changed path is not a prim path we will get an invalid prim back.
lib/mayaUsd/ufe/UsdContextOps.cpp
Outdated
@@ -184,6 +260,17 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) | |||
return visibility->setCmd( | |||
current == UsdGeomTokens->invisible ? UsdGeomTokens->inherited : UsdGeomTokens->invisible); | |||
} // Visibility | |||
else if (!itemPath.empty() && (itemPath[0] == "Add New Prim")) { |
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.
Super minor comment: it may be a good idea to keep these operation strings (like "Add New Prim") in a unique place. That way it'd be easier to change if we ever need to. I've seen the "Add New Prim" string already three times during this review.
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.
Yes good idea. See commit 14d8d4b
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.
Except for a minor comment, it looks good to me. Thanks!
* Code review comment - use string constants.
…outliner * Added context menu item for opening USD Layer Editor.
Since this all deals with context menu changes, I've also added one more small change in this commit for opening the USD Layer Editor. |
// Ufe::ContextItem strings | ||
// - the "Item" describe the operation to be performed. | ||
// - the "Label" is used in the context menu (can be localized). | ||
static constexpr char kUSDLayerEditorItem[] = "USD Layer Editor"; |
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.
Nit-pick: is this some MSVC-specific requirement? Because otherwise, you're in an unnamed namespace, you don't need the static linkage specifier.
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.
thanks! looks good to me
* Added missing PYTHONPATH for test.
* testAddNewPrim requires changes not in Maya yet.
5744a52
to
7918a30
Compare
MAYA-104214 - As a user, I'd like to add a USD prim to an existing stage