From 19efb65bc15160c090ea8ccfa20c2249322199d2 Mon Sep 17 00:00:00 2001 From: Jonathan Feldstein Date: Tue, 5 Jul 2022 10:13:23 -0400 Subject: [PATCH 1/6] LOOKDEVX-730 | Added new interface for creating materials/material nodes. --- lib/mayaUsd/ufe/CMakeLists.txt | 18 ++-- lib/mayaUsd/ufe/UsdContextOps.cpp | 61 ++++++++++++- .../ufe/UsdUndoCreateMaterialNodeCommand.cpp | 91 +++++++++++++++++++ .../ufe/UsdUndoCreateMaterialNodeCommand.h | 73 +++++++++++++++ 4 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp create mode 100644 lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index fb7d5a63c2..3af55ff6e6 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -124,6 +124,7 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) PRIVATE UsdConnections.cpp UsdConnectionHandler.cpp + UsdUndoCreateMaterialNodeCommand.cpp ) endif() endif() @@ -214,15 +215,6 @@ if (UFE_LIGHTS_SUPPORT) ) endif() -if(CMAKE_UFE_V4_FEATURES_AVAILABLE) - if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4020) - list(APPEND HEADERS - UsdConnections.h - UsdConnectionHandler.h - ) - endif() -endif() - if(CMAKE_UFE_V4_FEATURES_AVAILABLE) if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4001) list(APPEND HEADERS @@ -244,6 +236,14 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) ProxyShapeSceneSegmentHandler.h ) endif() + + if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4020) + list(APPEND HEADERS + UsdConnections.h + UsdConnectionHandler.h + UsdUndoCreateMaterialNodeCommand.h + ) + endif() endif() # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index ddfc36267c..d6da04968a 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -125,8 +126,22 @@ static constexpr char kAddMayaReferenceItem[] = "Add Maya Reference"; static constexpr char kAddMayaReferenceLabel[] = "Add Maya Reference..."; #endif #if PXR_VERSION >= 2108 -static constexpr char kBindMaterialToSelectionItem[] = "Assign Material to Selection"; -static constexpr char kBindMaterialToSelectionLabel[] = "Assign Material to Selection"; +static constexpr char kBindMaterialToSelectionItem[] = "Assign Material to Selection"; +static constexpr char kBindMaterialToSelectionLabel[] = "Assign Material to Selection"; +static constexpr char kAssignNewMaterialItem[] = "Assign New Material"; +static constexpr char kAssignNewMaterialLabel[] = "Assign New Material"; +static constexpr char kAssignNewUsdMaterialItem[] = "USD Material"; +static constexpr char kAssignNewUsdMaterialLabel[] = "USD"; +static constexpr char kAssignNewMaterialXMaterialItem[] = "MaterialX Material"; +static constexpr char kAssignNewMaterialXMaterialLabel[] = "MaterialX"; +static constexpr char kAssignNewArnoldMaterialItem[] = "Arnold Material"; +static constexpr char kAssignNewArnoldMaterialLabel[] = "Arnold"; +static constexpr char kAssignNewUsdPreviewSurfaceMaterialItem[] = "Usd Preview Surface"; +static constexpr char kAssignNewUsdPreviewSurfaceMaterialLabel[] = "Usd Preview Surface"; +static constexpr char kAssignNewStandardSurfaceMaterialItem[] = "Standard Surface"; +static constexpr char kAssignNewStandardSurfaceMaterialLabel[] = "Standard Surface"; +static constexpr char kAssignNewAIStandardSurfaceMaterialItem[] = "AI Standard Surface"; +static constexpr char kAssignNewAIStandardSurfaceMaterialLabel[] = "AI Standard Surface"; #endif #if PXR_VERSION >= 2008 @@ -963,6 +978,8 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& ClearAllReferencesUndoableCommand::commandName); } + items.emplace_back(Ufe::ContextItem::kSeparator); + items.emplace_back(kAssignNewMaterialItem, kAssignNewMaterialLabel, Ufe::ContextItem::kHasChildren); } else { if (itemPath[0] == kUSDVariantSetsItem) { UsdVariantSets varSets = prim().GetVariantSets(); @@ -1079,6 +1096,16 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& } } } + } else if (itemPath.size() == 1u && itemPath[0] == kAssignNewMaterialItem) { + items.emplace_back(kAssignNewUsdMaterialItem, kAssignNewUsdMaterialLabel, Ufe::ContextItem::kHasChildren); + items.emplace_back(kAssignNewMaterialXMaterialItem, kAssignNewMaterialXMaterialLabel, Ufe::ContextItem::kHasChildren); + items.emplace_back(kAssignNewArnoldMaterialItem, kAssignNewArnoldMaterialLabel, Ufe::ContextItem::kHasChildren); + } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewUsdMaterialItem) { + items.emplace_back(kAssignNewUsdPreviewSurfaceMaterialItem, kAssignNewUsdPreviewSurfaceMaterialLabel); + } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewMaterialXMaterialItem) { + items.emplace_back(kAssignNewStandardSurfaceMaterialItem, kAssignNewStandardSurfaceMaterialLabel); + } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewArnoldMaterialItem) { + items.emplace_back(kAssignNewAIStandardSurfaceMaterialItem, kAssignNewAIStandardSurfaceMaterialLabel); } #endif } // Top-level items @@ -1205,6 +1232,36 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return compositeCmd; } else if (itemPath[0] == UnbindMaterialUndoableCommand::commandName) { return std::make_shared(fItem->prim()); + } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewUsdPreviewSurfaceMaterialItem) { + if (fItem) + { + return UsdUndoCreateMaterialNodeCommand::create( + fItem, + "material", + "ND_standard_surface_surfaceshader", + Ufe::PathComponent("standardSurface") + ); + } + } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewStandardSurfaceMaterialItem) { + if (fItem) + { + return UsdUndoCreateMaterialNodeCommand::create( + fItem, + "material", + "ND_standard_surface_surfaceshader", + Ufe::PathComponent("standardSurface") + ); + } + } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewAIStandardSurfaceMaterialItem) { + if (fItem) + { + return UsdUndoCreateMaterialNodeCommand::create( + fItem, + "material", + "ND_standard_surface_surfaceshader", + Ufe::PathComponent("standardSurface") + ); + } } #endif return nullptr; diff --git a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp new file mode 100644 index 0000000000..d21a81e599 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2019 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "UsdUndoCreateMaterialNodeCommand.h" + +#include +#include + +#include +#include + +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +UsdUndoCreateMaterialNodeCommand::UsdUndoCreateMaterialNodeCommand( + const UsdSceneItem::Ptr& parentItem, + const std::string& materialBaseName, + const std::string& materialNodeType, + const Ufe::PathComponent& materialNodeName) + : Ufe::InsertChildCommand() + , _parentItem(parentItem) + , _materialBaseName(materialBaseName) + , _materialNodeType(materialNodeType) + , _materialNodeName(materialNodeName) + , _materialNodeCompositeCmd(std::make_shared()) +{ +} + +UsdUndoCreateMaterialNodeCommand::~UsdUndoCreateMaterialNodeCommand() { } + +UsdUndoCreateMaterialNodeCommand::Ptr UsdUndoCreateMaterialNodeCommand::create( + const UsdSceneItem::Ptr& parentItem, + const std::string& materialBaseName, + const std::string& materialNodeType, + const Ufe::PathComponent& materialNodeName) +{ + // Changing the hierarchy of invalid items is not allowed. + if (!parentItem || !parentItem->prim().IsActive()) + return nullptr; + + return std::make_shared(parentItem, materialBaseName, materialNodeType, materialNodeName); +} + +Ufe::SceneItem::Ptr UsdUndoCreateMaterialNodeCommand::insertedChild() const { return _materialNodeItem; } + +void UsdUndoCreateMaterialNodeCommand::execute() +{ + const auto materialCmd = UsdUndoAddNewPrimCommand::create(_parentItem, _materialBaseName, "Material"); + _materialNodeCompositeCmd->append(materialCmd); + materialCmd->execute(); + _materialItem = UsdSceneItem::create(materialCmd->newUfePath(), materialCmd->newPrim()); + PXR_NS::SdrRegistry ®istry = PXR_NS::SdrRegistry::GetInstance(); + SdrShaderNodeConstPtr shaderNode = registry.GetShaderNodeByIdentifier(PXR_NS::TfToken(_materialNodeType.c_str())); + if (shaderNode) { + const auto materialNodeCmd = UsdUndoCreateFromNodeDefCommand::create( + shaderNode, _materialItem, _materialNodeName); + _materialNodeCompositeCmd->append(materialNodeCmd); + materialNodeCmd->execute(); + _materialNodeItem = materialNodeCmd->insertedChild(); + Ufe::Attributes::Ptr materialAttributes = Ufe::Attributes::attributes(_materialItem); + Ufe::Attributes::Ptr materialNodeAttributes = Ufe::Attributes::attributes(_materialNodeItem); + Ufe::ConnectCommand::Ptr connectCommand = std::make_shared( + materialNodeAttributes->attribute("out"), materialAttributes->attribute("surface")); + _materialNodeCompositeCmd->append(connectCommand); + connectCommand->execute(); + } +} + +void UsdUndoCreateMaterialNodeCommand::undo() { _materialNodeCompositeCmd->undo(); } + +void UsdUndoCreateMaterialNodeCommand::redo() { _materialNodeCompositeCmd->redo(); } + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h new file mode 100644 index 0000000000..305dc37c7c --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h @@ -0,0 +1,73 @@ +// +// Copyright 2019 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include +#include + +#include +#include +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief UsdUndoCreateMaterialNodeCommand +class MAYAUSD_CORE_PUBLIC UsdUndoCreateMaterialNodeCommand : public Ufe::InsertChildCommand +{ +public: + typedef std::shared_ptr Ptr; + + UsdUndoCreateMaterialNodeCommand( + const UsdSceneItem::Ptr& parentItem, + const std::string& materialBaseName, + const std::string& materialNodeType, + const Ufe::PathComponent& materialNodeName); + ~UsdUndoCreateMaterialNodeCommand() override; + + // Delete the copy/move constructors assignment operators. + UsdUndoCreateMaterialNodeCommand(const UsdUndoCreateMaterialNodeCommand&) = delete; + UsdUndoCreateMaterialNodeCommand& operator=(const UsdUndoCreateMaterialNodeCommand&) = delete; + UsdUndoCreateMaterialNodeCommand(UsdUndoCreateMaterialNodeCommand&&) = delete; + UsdUndoCreateMaterialNodeCommand& operator=(UsdUndoCreateMaterialNodeCommand&&) = delete; + + //! Create a UsdUndoCreateGroupCommand from a USD scene item and a UFE path component. + static UsdUndoCreateMaterialNodeCommand::Ptr create( + const UsdSceneItem::Ptr& parentItem, + const std::string& materialBaseName, + const std::string& materialNodeType, + const Ufe::PathComponent& materialNodeName); + + Ufe::SceneItem::Ptr insertedChild() const override; + + void execute() override; + void undo() override; + void redo() override; + +private: + UsdSceneItem::Ptr _parentItem; + const std::string _materialBaseName; + const std::string _materialNodeType; + const Ufe::PathComponent _materialNodeName; + UsdSceneItem::Ptr _materialItem; + Ufe::SceneItem::Ptr _materialNodeItem; + + std::shared_ptr _materialNodeCompositeCmd; + +}; // UsdUndoCreateMaterialNodeCommand + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF From a2140f957fdb2e5cbb352bd89f40cfc088cf44b8 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Thu, 7 Jul 2022 18:15:23 -0400 Subject: [PATCH 2/6] Completed implementation - Puts the material in the right location - Supports undo/redo - Added unit test --- lib/mayaUsd/ufe/CMakeLists.txt | 15 +- lib/mayaUsd/ufe/UsdContextOps.cpp | 509 +++++++++--------- .../ufe/UsdUndoCreateMaterialNodeCommand.cpp | 91 ---- .../ufe/UsdUndoCreateMaterialNodeCommand.h | 73 --- lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp | 374 +++++++++++++ lib/mayaUsd/ufe/UsdUndoMaterialCommands.h | 127 +++++ test/lib/ufe/testContextOps.py | 110 ++++ 7 files changed, 876 insertions(+), 423 deletions(-) delete mode 100644 lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp delete mode 100644 lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h create mode 100644 lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp create mode 100644 lib/mayaUsd/ufe/UsdUndoMaterialCommands.h diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index 3af55ff6e6..a92fb0defe 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -124,11 +124,17 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) PRIVATE UsdConnections.cpp UsdConnectionHandler.cpp - UsdUndoCreateMaterialNodeCommand.cpp ) endif() endif() +if(PXR_VERSION GREATER_EQUAL 2108) + target_sources(${PROJECT_NAME} + PRIVATE + UsdUndoMaterialCommands.cpp + ) +endif() + set(HEADERS Global.h ProxyShapeHandler.h @@ -241,11 +247,16 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) list(APPEND HEADERS UsdConnections.h UsdConnectionHandler.h - UsdUndoCreateMaterialNodeCommand.h ) endif() endif() +if(PXR_VERSION GREATER_EQUAL 2108) + list(APPEND HEADERS + UsdUndoMaterialCommands.h + ) +endif() + # ----------------------------------------------------------------------------- # promote headers # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index d6da04968a..d6d4466b89 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -21,11 +21,13 @@ #include #include #endif +#if PXR_VERSION >= 2108 +#include +#endif #include #include #include #include -#include #include #include @@ -36,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -52,6 +57,7 @@ #include #include #include +#include #include #include @@ -126,22 +132,20 @@ static constexpr char kAddMayaReferenceItem[] = "Add Maya Reference"; static constexpr char kAddMayaReferenceLabel[] = "Add Maya Reference..."; #endif #if PXR_VERSION >= 2108 -static constexpr char kBindMaterialToSelectionItem[] = "Assign Material to Selection"; -static constexpr char kBindMaterialToSelectionLabel[] = "Assign Material to Selection"; -static constexpr char kAssignNewMaterialItem[] = "Assign New Material"; -static constexpr char kAssignNewMaterialLabel[] = "Assign New Material"; -static constexpr char kAssignNewUsdMaterialItem[] = "USD Material"; -static constexpr char kAssignNewUsdMaterialLabel[] = "USD"; -static constexpr char kAssignNewMaterialXMaterialItem[] = "MaterialX Material"; -static constexpr char kAssignNewMaterialXMaterialLabel[] = "MaterialX"; -static constexpr char kAssignNewArnoldMaterialItem[] = "Arnold Material"; -static constexpr char kAssignNewArnoldMaterialLabel[] = "Arnold"; -static constexpr char kAssignNewUsdPreviewSurfaceMaterialItem[] = "Usd Preview Surface"; -static constexpr char kAssignNewUsdPreviewSurfaceMaterialLabel[] = "Usd Preview Surface"; -static constexpr char kAssignNewStandardSurfaceMaterialItem[] = "Standard Surface"; -static constexpr char kAssignNewStandardSurfaceMaterialLabel[] = "Standard Surface"; -static constexpr char kAssignNewAIStandardSurfaceMaterialItem[] = "AI Standard Surface"; -static constexpr char kAssignNewAIStandardSurfaceMaterialLabel[] = "AI Standard Surface"; +static constexpr char kBindMaterialToSelectionItem[] = "Assign Material to Selection"; +static constexpr char kBindMaterialToSelectionLabel[] = "Assign Material to Selection"; +static constexpr char kAssignNewMaterialItem[] = "Assign New Material"; +static constexpr char kAssignNewMaterialLabel[] = "Assign New Material"; +static constexpr char kAssignNewUsdMaterialItem[] = "USD Material"; +static constexpr char kAssignNewUsdMaterialLabel[] = "USD"; +static constexpr char kAssignNewMaterialXMaterialItem[] = "MaterialX Material"; +static constexpr char kAssignNewMaterialXMaterialLabel[] = "MaterialX"; +static constexpr char kAssignNewArnoldMaterialItem[] = "Arnold Material"; +static constexpr char kAssignNewArnoldMaterialLabel[] = "Arnold"; +static constexpr char kAssignNewUsdPreviewSurfaceMaterialItem[] = "UsdPreviewSurface"; +static constexpr char kAssignNewUsdPreviewSurfaceMaterialLabel[] = "Usd Preview Surface"; +static constexpr char kAssignNewAIStandardSurfaceMaterialItem[] = "arnold:standard_surface"; +static constexpr char kAssignNewAIStandardSurfaceMaterialLabel[] = "AI Standard Surface"; #endif #if PXR_VERSION >= 2008 @@ -188,6 +192,137 @@ struct WaitCursor ~WaitCursor() { MGlobal::executeCommand("waitCursor -state 0"); } }; +//! \brief This check has a 3 seconds slowdown to load all Sdr nodes in the registry. We do it in +/// advance in order to not have this 3 seconds delay when the "Assign New Material" submenu +/// is built for the first time. +bool _hasArnoldShaders() +{ + auto findArnold = []() { + const auto& sdrRegistry = PXR_NS::SdrRegistry::GetInstance(); + auto sourceTypes = sdrRegistry.GetAllNodeSourceTypes(); + return std::find(sourceTypes.cbegin(), sourceTypes.cend(), TfToken("arnold")) + != sourceTypes.cend(); + }; + static const bool kHasArnoldShaders = findArnold(); + return kHasArnoldShaders; +}; + +struct MxShaderMenuEntry +{ + MxShaderMenuEntry(const std::string& label, const std::string& identifier) + : _label(label) + , _identifier(identifier) + { + } + std::string _label; + const std::string& _identifier; +}; +typedef std::vector MxShaderMenuEntryVec; + +const MxShaderMenuEntryVec& getMaterialXSurfaceShaders() +{ + static MxShaderMenuEntryVec mxSurfaceShaders; + if (mxSurfaceShaders.empty()) { + auto& sdrRegistry = PXR_NS::SdrRegistry::GetInstance(); + // Here is a list of nodes we know work fine as starting materials for the contextual menu. + // We might add discovery code later, but this discovery code will have the difficult task + // of filtering out: + // - utility nodes like ND_add_surfaceshader + // - basic building blocks like ND_thin_surface + // - shaders that exist only as pure definitions like ND_disney_bsdf_2015_surface + const std::vector vettedSurfaces = { "ND_standard_surface_surfaceshader", + "ND_gltf_pbr_surfaceshader", + "ND_UsdPreviewSurface_surfaceshader" }; + for (auto&& identifier : vettedSurfaces) { + auto shaderDef = sdrRegistry.GetShaderNodeByIdentifier(TfToken(identifier)); + if (!shaderDef) { + continue; + } + std::string label = UsdMayaUtil::prettifyName(shaderDef->GetFamily().GetString()); + mxSurfaceShaders.emplace_back(label, shaderDef->GetIdentifier().GetString()); + } + } + return mxSurfaceShaders; +} + +//! \brief Create an Item and select it: +class BaseCreateAndSelectUndoableCommand : public Ufe::UndoableCommand +{ +public: + BaseCreateAndSelectUndoableCommand(const Ufe::UndoableCommand::Ptr& creationCmd) + : _creationCmd(creationCmd) + { + } + + void redo() override + { + if (_selectionCmd) { + _creationCmd->redo(); + _selectionCmd->redo(); + } + } + + void undo() override + { + if (_selectionCmd) { + _selectionCmd->undo(); + _creationCmd->undo(); + } + } + +protected: + Ufe::UndoableCommand::Ptr _creationCmd; + Ufe::UndoableCommand::Ptr _selectionCmd; +}; + +//! \brief Create a Prim and select it: +class UsdUndoAddNewPrimAndSelectCommand : public BaseCreateAndSelectUndoableCommand +{ +public: + UsdUndoAddNewPrimAndSelectCommand( + const MAYAUSD_NS::ufe::UsdUndoAddNewPrimCommand::Ptr& creationCmd) + : BaseCreateAndSelectUndoableCommand(creationCmd) + { + } + + void execute() override + { + _creationCmd->execute(); + auto addPrimCmd + = std::dynamic_pointer_cast(_creationCmd); + // Create the selection command only if the creation succeeded: + if (!addPrimCmd->newUfePath().empty()) { + Ufe::Selection newSelection; + newSelection.append(Ufe::Hierarchy::createItem(addPrimCmd->newUfePath())); + _selectionCmd = Ufe::SelectionReplaceWith::createAndExecute( + Ufe::GlobalSelection::get(), newSelection); + } + } +}; + +//! \brief Create a working Material and select it: +class InsertChildAndSelectCommand : public BaseCreateAndSelectUndoableCommand +{ +public: + InsertChildAndSelectCommand(const Ufe::InsertChildCommand::Ptr& creationCmd) + : BaseCreateAndSelectUndoableCommand(creationCmd) + { + } + + void execute() override + { + _creationCmd->execute(); + auto insertChildCmd = std::dynamic_pointer_cast(_creationCmd); + // Create the selection command only if the creation succeeded: + if (insertChildCmd->insertedChild()) { + Ufe::Selection newSelection; + newSelection.append(insertChildCmd->insertedChild()); + _selectionCmd = Ufe::SelectionReplaceWith::createAndExecute( + Ufe::GlobalSelection::get(), newSelection); + } + } +}; + //! \brief Undoable command for loading a USD prim. class LoadUnloadBaseUndoableCommand : public Ufe::UndoableCommand { @@ -515,148 +650,6 @@ class ClearAllReferencesUndoableCommand : public Ufe::UndoableCommand const std::string ClearAllReferencesUndoableCommand::commandName("Clear All References"); const MString ClearAllReferencesUndoableCommand::cancelRemoval("No"); -#if PXR_VERSION >= 2108 -class BindMaterialUndoableCommand : public Ufe::UndoableCommand -{ -public: - static const std::string commandName; - - static UsdPrim CompatiblePrim(const Ufe::SceneItem::Ptr& item) - { - auto usdItem = std::dynamic_pointer_cast(item); - if (!usdItem) { - return {}; - } - UsdPrim usdPrim = usdItem->prim(); - if (UsdShadeNodeGraph(usdPrim) || UsdShadeShader(usdPrim)) { - // The binding schema can be applied anywhere, but it makes no sense on a - // material or a shader. - return {}; - } - if (PXR_NS::UsdShadeMaterialBindingAPI::CanApply(usdPrim)) { - return usdPrim; - } - return {}; - } - - BindMaterialUndoableCommand(const UsdPrim& prim, const SdfPath& materialPath) - : _stage(prim.GetStage()) - , _primPath(prim.GetPath()) - , _materialPath(materialPath) - { - } - - void undo() override - { - if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { - return; - } - - UsdPrim prim = _stage->GetPrimAtPath(_primPath); - if (prim.IsValid()) { - auto bindingAPI = UsdShadeMaterialBindingAPI(prim); - if (bindingAPI) { - if (_previousMaterialPath.IsEmpty()) { - bindingAPI.UnbindDirectBinding(); - } else { - UsdShadeMaterial material(_stage->GetPrimAtPath(_previousMaterialPath)); - bindingAPI.Bind(material); - } - } - if (_appliedBindingAPI) { - prim.RemoveAPI(); - } - } - } - - void redo() override - { - if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { - return; - } - - UsdPrim prim = _stage->GetPrimAtPath(_primPath); - UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); - if (prim.IsValid() && material) { - UsdShadeMaterialBindingAPI bindingAPI; - if (prim.HasAPI()) { - bindingAPI = UsdShadeMaterialBindingAPI(prim); - _previousMaterialPath = bindingAPI.GetDirectBinding().GetMaterialPath(); - } else { - bindingAPI = UsdShadeMaterialBindingAPI::Apply(prim); - _appliedBindingAPI = true; - } - bindingAPI.Bind(material); - } - } - -private: - UsdStageWeakPtr _stage; - SdfPath _primPath; - SdfPath _materialPath; - SdfPath _previousMaterialPath; - bool _appliedBindingAPI = false; -}; -const std::string BindMaterialUndoableCommand::commandName("Bind Material"); - -class UnbindMaterialUndoableCommand : public Ufe::UndoableCommand -{ -public: - static const std::string commandName; - - UnbindMaterialUndoableCommand(const UsdPrim& prim) - : _stage(prim.GetStage()) - , _primPath(prim.GetPath()) - { - } - - void undo() override - { - if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { - return; - } - - UsdPrim prim = _stage->GetPrimAtPath(_primPath); - UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); - if (prim.IsValid() && material) { - // BindingAPI is still there since we did not remove it. - auto bindingAPI = UsdShadeMaterialBindingAPI(prim); - UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); - if (bindingAPI && material) { - bindingAPI.Bind(material); - } - } - } - - void redo() override - { - if (!_stage || _primPath.IsEmpty()) { - return; - } - - UsdPrim prim = _stage->GetPrimAtPath(_primPath); - if (prim.IsValid()) { - auto bindingAPI = UsdShadeMaterialBindingAPI(prim); - if (bindingAPI) { - auto materialBinding = bindingAPI.GetDirectBinding(); - _materialPath = materialBinding.GetMaterialPath(); - if (!_materialPath.IsEmpty()) { - bindingAPI.UnbindDirectBinding(); - // TODO: Can we remove the BindingAPI at this point? - // Not easy to know for sure. - } - } - } - } - -private: - UsdStageWeakPtr _stage; - SdfPath _primPath; - SdfPath _materialPath; -}; -const std::string UnbindMaterialUndoableCommand::commandName("Unbind Material"); -#endif - std::vector> _computeLoadAndUnloadItems(const UsdPrim& prim) { @@ -907,79 +900,90 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& // Top level item - Add New Prim (for all context op types). items.emplace_back(kUSDAddNewPrimItem, kUSDAddNewPrimLabel, Ufe::ContextItem::kHasChildren); - + if (!fIsAGatewayType) { + items.emplace_back( + AddUsdReferenceUndoableCommand::commandName, + AddUsdReferenceUndoableCommand::commandName); + items.emplace_back( + ClearAllReferencesUndoableCommand::commandName, + ClearAllReferencesUndoableCommand::commandName); + } #if PXR_VERSION >= 2108 - // Top level item - Bind/unbind existing materials - if (PXR_NS::UsdShadeMaterialBindingAPI::CanApply(fItem->prim())) { - // Show bind menu if there is at least one bindable material in the stage. - // - // TODO: Show only materials that are inside of the asset's namespace otherwise there - // will be "refers to a path outside the scope" errors. See - // https://groups.google.com/g/usd-interest/c/dmjV5bQBKIo/m/LeozZ3k6BAAJ - // This might help restrict the stage traversal scope and improve performance. - // - // For completeness, and to point out that material assignments are complex: - // - // TODO: Introduce the "rendering purpose" concept - // TODO: Introduce material binding via collections API - // - // Find materials in the global selection. Either directly selected or a direct child of - // the selection. This way we limit how many items we traverse in search of something to - // bind. - bool foundMaterialItem = false; - if (auto globalSn = Ufe::GlobalSelection::get()) { - for (auto&& selItem : *globalSn) { - UsdSceneItem::Ptr usdItem = std::dynamic_pointer_cast(selItem); - if (!usdItem) { - continue; - } - UsdShadeMaterial material(usdItem->prim()); - if (material) { - foundMaterialItem = true; - break; - } - for (auto&& usdChild : usdItem->prim().GetChildren()) { - UsdShadeMaterial material(usdChild); + if (!fIsAGatewayType) { + // Top level item - Bind/unbind existing materials + bool materialSeparatorsAdded = false; + if (PXR_NS::UsdShadeMaterialBindingAPI::CanApply(fItem->prim())) { + // Show bind menu if there is at least one bindable material in the stage. + // + // TODO: Show only materials that are inside of the asset's namespace otherwise + // there will be "refers to a path outside the scope" errors. See + // https://groups.google.com/g/usd-interest/c/dmjV5bQBKIo/m/LeozZ3k6BAAJ + // This might help restrict the stage traversal scope and improve performance. + // + // For completeness, and to point out that material assignments are complex: + // + // TODO: Introduce the "rendering purpose" concept + // TODO: Introduce material binding via collections API + // + // Find materials in the global selection. Either directly selected or a direct + // child of the selection. This way we limit how many items we traverse in search of + // something to bind. + if (!materialSeparatorsAdded) { + items.emplace_back(Ufe::ContextItem::kSeparator); + materialSeparatorsAdded = true; + } + bool foundMaterialItem = false; + if (auto globalSn = Ufe::GlobalSelection::get()) { + for (auto&& selItem : *globalSn) { + UsdSceneItem::Ptr usdItem + = std::dynamic_pointer_cast(selItem); + if (!usdItem) { + continue; + } + UsdShadeMaterial material(usdItem->prim()); if (material) { foundMaterialItem = true; break; } + for (auto&& usdChild : usdItem->prim().GetChildren()) { + UsdShadeMaterial material(usdChild); + if (material) { + foundMaterialItem = true; + break; + } + } + if (foundMaterialItem) { + break; + } } if (foundMaterialItem) { - break; + items.emplace_back( + BindMaterialUndoableCommand::commandName, + BindMaterialUndoableCommand::commandName, + Ufe::ContextItem::kHasChildren); } } - if (foundMaterialItem) { + items.emplace_back( + kAssignNewMaterialItem, + kAssignNewMaterialLabel, + Ufe::ContextItem::kHasChildren); + } + if (fItem->prim().HasAPI()) { + UsdShadeMaterialBindingAPI bindingAPI(fItem->prim()); + // Show unbind menu item if there is a direct binding relationship: + auto directBinding = bindingAPI.GetDirectBinding(); + if (directBinding.GetMaterial()) { + if (!materialSeparatorsAdded) { + items.emplace_back(Ufe::ContextItem::kSeparator); + materialSeparatorsAdded = true; + } items.emplace_back( - BindMaterialUndoableCommand::commandName, - BindMaterialUndoableCommand::commandName, - Ufe::ContextItem::kHasChildren); + UnbindMaterialUndoableCommand::commandName, + UnbindMaterialUndoableCommand::commandName); } } } - if (fItem->prim().HasAPI()) { - UsdShadeMaterialBindingAPI bindingAPI(fItem->prim()); - // Show unbind menu item if there is a direct binding relationship: - auto directBinding = bindingAPI.GetDirectBinding(); - if (directBinding.GetMaterial()) { - items.emplace_back( - UnbindMaterialUndoableCommand::commandName, - UnbindMaterialUndoableCommand::commandName); - } - } #endif - - if (!fIsAGatewayType) { - items.emplace_back( - AddUsdReferenceUndoableCommand::commandName, - AddUsdReferenceUndoableCommand::commandName); - items.emplace_back( - ClearAllReferencesUndoableCommand::commandName, - ClearAllReferencesUndoableCommand::commandName); - } - - items.emplace_back(Ufe::ContextItem::kSeparator); - items.emplace_back(kAssignNewMaterialItem, kAssignNewMaterialLabel, Ufe::ContextItem::kHasChildren); } else { if (itemPath[0] == kUSDVariantSetsItem) { UsdVariantSets varSets = prim().GetVariantSets(); @@ -1097,15 +1101,30 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& } } } else if (itemPath.size() == 1u && itemPath[0] == kAssignNewMaterialItem) { - items.emplace_back(kAssignNewUsdMaterialItem, kAssignNewUsdMaterialLabel, Ufe::ContextItem::kHasChildren); - items.emplace_back(kAssignNewMaterialXMaterialItem, kAssignNewMaterialXMaterialLabel, Ufe::ContextItem::kHasChildren); - items.emplace_back(kAssignNewArnoldMaterialItem, kAssignNewArnoldMaterialLabel, Ufe::ContextItem::kHasChildren); - } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewUsdMaterialItem) { - items.emplace_back(kAssignNewUsdPreviewSurfaceMaterialItem, kAssignNewUsdPreviewSurfaceMaterialLabel); - } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewMaterialXMaterialItem) { - items.emplace_back(kAssignNewStandardSurfaceMaterialItem, kAssignNewStandardSurfaceMaterialLabel); - } else if(itemPath.size() == 2u && itemPath[1] == kAssignNewArnoldMaterialItem) { - items.emplace_back(kAssignNewAIStandardSurfaceMaterialItem, kAssignNewAIStandardSurfaceMaterialLabel); + items.emplace_back( + kAssignNewUsdMaterialItem, + kAssignNewUsdMaterialLabel, + Ufe::ContextItem::kHasChildren); + items.emplace_back( + kAssignNewMaterialXMaterialItem, + kAssignNewMaterialXMaterialLabel, + Ufe::ContextItem::kHasChildren); + if (_hasArnoldShaders()) { + items.emplace_back( + kAssignNewArnoldMaterialItem, + kAssignNewArnoldMaterialLabel, + Ufe::ContextItem::kHasChildren); + } + } else if (itemPath.size() == 2u && itemPath[1] == kAssignNewUsdMaterialItem) { + items.emplace_back( + kAssignNewUsdPreviewSurfaceMaterialItem, kAssignNewUsdPreviewSurfaceMaterialLabel); + } else if (itemPath.size() == 2u && itemPath[1] == kAssignNewMaterialXMaterialItem) { + for (auto&& menuEntry : getMaterialXSurfaceShaders()) { + items.emplace_back(menuEntry._identifier, menuEntry._label); + } + } else if (itemPath.size() == 2u && itemPath[1] == kAssignNewArnoldMaterialItem) { + items.emplace_back( + kAssignNewAIStandardSurfaceMaterialItem, kAssignNewAIStandardSurfaceMaterialLabel); } #endif } // Top-level items @@ -1160,7 +1179,8 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) } // At this point we know the last item in the itemPath is the prim type to create auto primType = itemPath[itemPath.size() - 1]; - return UsdUndoAddNewPrimCommand::create(fItem, primType, primType); + return std::make_shared( + UsdUndoAddNewPrimCommand::create(fItem, primType, primType)); #ifdef WANT_QT_BUILD // When building without Qt there is no LayerEditor } else if (itemPath[0] == kUSDLayerEditorItem) { @@ -1232,35 +1252,10 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return compositeCmd; } else if (itemPath[0] == UnbindMaterialUndoableCommand::commandName) { return std::make_shared(fItem->prim()); - } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewUsdPreviewSurfaceMaterialItem) { - if (fItem) - { - return UsdUndoCreateMaterialNodeCommand::create( - fItem, - "material", - "ND_standard_surface_surfaceshader", - Ufe::PathComponent("standardSurface") - ); - } - } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewStandardSurfaceMaterialItem) { - if (fItem) - { - return UsdUndoCreateMaterialNodeCommand::create( - fItem, - "material", - "ND_standard_surface_surfaceshader", - Ufe::PathComponent("standardSurface") - ); - } - } else if(itemPath.size() == 3u && itemPath[2] == kAssignNewAIStandardSurfaceMaterialItem) { - if (fItem) - { - return UsdUndoCreateMaterialNodeCommand::create( - fItem, - "material", - "ND_standard_surface_surfaceshader", - Ufe::PathComponent("standardSurface") - ); + } else if (itemPath.size() == 3u && itemPath[0] == kAssignNewMaterialItem) { + if (fItem) { + return std::make_shared( + UsdUndoAssignNewMaterialCommand::create(fItem, itemPath[2])); } } #endif diff --git a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp deleted file mode 100644 index d21a81e599..0000000000 --- a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright 2019 Autodesk -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#include "UsdUndoCreateMaterialNodeCommand.h" - -#include -#include - -#include -#include - -#include -#include - -PXR_NAMESPACE_USING_DIRECTIVE - -namespace MAYAUSD_NS_DEF { -namespace ufe { - -UsdUndoCreateMaterialNodeCommand::UsdUndoCreateMaterialNodeCommand( - const UsdSceneItem::Ptr& parentItem, - const std::string& materialBaseName, - const std::string& materialNodeType, - const Ufe::PathComponent& materialNodeName) - : Ufe::InsertChildCommand() - , _parentItem(parentItem) - , _materialBaseName(materialBaseName) - , _materialNodeType(materialNodeType) - , _materialNodeName(materialNodeName) - , _materialNodeCompositeCmd(std::make_shared()) -{ -} - -UsdUndoCreateMaterialNodeCommand::~UsdUndoCreateMaterialNodeCommand() { } - -UsdUndoCreateMaterialNodeCommand::Ptr UsdUndoCreateMaterialNodeCommand::create( - const UsdSceneItem::Ptr& parentItem, - const std::string& materialBaseName, - const std::string& materialNodeType, - const Ufe::PathComponent& materialNodeName) -{ - // Changing the hierarchy of invalid items is not allowed. - if (!parentItem || !parentItem->prim().IsActive()) - return nullptr; - - return std::make_shared(parentItem, materialBaseName, materialNodeType, materialNodeName); -} - -Ufe::SceneItem::Ptr UsdUndoCreateMaterialNodeCommand::insertedChild() const { return _materialNodeItem; } - -void UsdUndoCreateMaterialNodeCommand::execute() -{ - const auto materialCmd = UsdUndoAddNewPrimCommand::create(_parentItem, _materialBaseName, "Material"); - _materialNodeCompositeCmd->append(materialCmd); - materialCmd->execute(); - _materialItem = UsdSceneItem::create(materialCmd->newUfePath(), materialCmd->newPrim()); - PXR_NS::SdrRegistry ®istry = PXR_NS::SdrRegistry::GetInstance(); - SdrShaderNodeConstPtr shaderNode = registry.GetShaderNodeByIdentifier(PXR_NS::TfToken(_materialNodeType.c_str())); - if (shaderNode) { - const auto materialNodeCmd = UsdUndoCreateFromNodeDefCommand::create( - shaderNode, _materialItem, _materialNodeName); - _materialNodeCompositeCmd->append(materialNodeCmd); - materialNodeCmd->execute(); - _materialNodeItem = materialNodeCmd->insertedChild(); - Ufe::Attributes::Ptr materialAttributes = Ufe::Attributes::attributes(_materialItem); - Ufe::Attributes::Ptr materialNodeAttributes = Ufe::Attributes::attributes(_materialNodeItem); - Ufe::ConnectCommand::Ptr connectCommand = std::make_shared( - materialNodeAttributes->attribute("out"), materialAttributes->attribute("surface")); - _materialNodeCompositeCmd->append(connectCommand); - connectCommand->execute(); - } -} - -void UsdUndoCreateMaterialNodeCommand::undo() { _materialNodeCompositeCmd->undo(); } - -void UsdUndoCreateMaterialNodeCommand::redo() { _materialNodeCompositeCmd->redo(); } - -} // namespace ufe -} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h b/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h deleted file mode 100644 index 305dc37c7c..0000000000 --- a/lib/mayaUsd/ufe/UsdUndoCreateMaterialNodeCommand.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright 2019 Autodesk -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#pragma once - -#include -#include - -#include -#include -#include - -namespace MAYAUSD_NS_DEF { -namespace ufe { - -//! \brief UsdUndoCreateMaterialNodeCommand -class MAYAUSD_CORE_PUBLIC UsdUndoCreateMaterialNodeCommand : public Ufe::InsertChildCommand -{ -public: - typedef std::shared_ptr Ptr; - - UsdUndoCreateMaterialNodeCommand( - const UsdSceneItem::Ptr& parentItem, - const std::string& materialBaseName, - const std::string& materialNodeType, - const Ufe::PathComponent& materialNodeName); - ~UsdUndoCreateMaterialNodeCommand() override; - - // Delete the copy/move constructors assignment operators. - UsdUndoCreateMaterialNodeCommand(const UsdUndoCreateMaterialNodeCommand&) = delete; - UsdUndoCreateMaterialNodeCommand& operator=(const UsdUndoCreateMaterialNodeCommand&) = delete; - UsdUndoCreateMaterialNodeCommand(UsdUndoCreateMaterialNodeCommand&&) = delete; - UsdUndoCreateMaterialNodeCommand& operator=(UsdUndoCreateMaterialNodeCommand&&) = delete; - - //! Create a UsdUndoCreateGroupCommand from a USD scene item and a UFE path component. - static UsdUndoCreateMaterialNodeCommand::Ptr create( - const UsdSceneItem::Ptr& parentItem, - const std::string& materialBaseName, - const std::string& materialNodeType, - const Ufe::PathComponent& materialNodeName); - - Ufe::SceneItem::Ptr insertedChild() const override; - - void execute() override; - void undo() override; - void redo() override; - -private: - UsdSceneItem::Ptr _parentItem; - const std::string _materialBaseName; - const std::string _materialNodeType; - const Ufe::PathComponent _materialNodeName; - UsdSceneItem::Ptr _materialItem; - Ufe::SceneItem::Ptr _materialNodeItem; - - std::shared_ptr _materialNodeCompositeCmd; - -}; // UsdUndoCreateMaterialNodeCommand - -} // namespace ufe -} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp new file mode 100644 index 0000000000..c1298f3aeb --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp @@ -0,0 +1,374 @@ +// +// Copyright 2019 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "UsdUndoMaterialCommands.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +UsdPrim BindMaterialUndoableCommand::CompatiblePrim(const Ufe::SceneItem::Ptr& item) +{ + auto usdItem = std::dynamic_pointer_cast(item); + if (!usdItem) { + return {}; + } + UsdPrim usdPrim = usdItem->prim(); + if (UsdShadeNodeGraph(usdPrim) || UsdShadeShader(usdPrim)) { + // The binding schema can be applied anywhere, but it makes no sense on a + // material or a shader. + return {}; + } + if (PXR_NS::UsdShadeMaterialBindingAPI::CanApply(usdPrim)) { + return usdPrim; + } + return {}; +} + +BindMaterialUndoableCommand::BindMaterialUndoableCommand( + const UsdPrim& prim, + const SdfPath& materialPath) + : _stage(prim.GetStage()) + , _primPath(prim.GetPath()) + , _materialPath(materialPath) +{ +} + +BindMaterialUndoableCommand::~BindMaterialUndoableCommand() { } + +void BindMaterialUndoableCommand::undo() +{ + if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { + return; + } + + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + if (prim.IsValid()) { + auto bindingAPI = UsdShadeMaterialBindingAPI(prim); + if (bindingAPI) { + if (_previousMaterialPath.IsEmpty()) { + bindingAPI.UnbindDirectBinding(); + } else { + UsdShadeMaterial material(_stage->GetPrimAtPath(_previousMaterialPath)); + bindingAPI.Bind(material); + } + } + if (_appliedBindingAPI) { + prim.RemoveAPI(); + } + } +} + +void BindMaterialUndoableCommand::redo() +{ + if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { + return; + } + + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); + if (prim.IsValid() && material) { + UsdShadeMaterialBindingAPI bindingAPI; + if (prim.HasAPI()) { + bindingAPI = UsdShadeMaterialBindingAPI(prim); + _previousMaterialPath = bindingAPI.GetDirectBinding().GetMaterialPath(); + } else { + bindingAPI = UsdShadeMaterialBindingAPI::Apply(prim); + _appliedBindingAPI = true; + } + bindingAPI.Bind(material); + } +} +const std::string BindMaterialUndoableCommand::commandName("Bind Material"); + +UnbindMaterialUndoableCommand::UnbindMaterialUndoableCommand(const UsdPrim& prim) + : _stage(prim.GetStage()) + , _primPath(prim.GetPath()) +{ +} + +UnbindMaterialUndoableCommand::~UnbindMaterialUndoableCommand() { } + +void UnbindMaterialUndoableCommand::undo() +{ + if (!_stage || _primPath.IsEmpty() || _materialPath.IsEmpty()) { + return; + } + + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); + if (prim.IsValid() && material) { + // BindingAPI is still there since we did not remove it. + auto bindingAPI = UsdShadeMaterialBindingAPI(prim); + UsdShadeMaterial material(_stage->GetPrimAtPath(_materialPath)); + if (bindingAPI && material) { + bindingAPI.Bind(material); + } + } +} + +void UnbindMaterialUndoableCommand::redo() +{ + if (!_stage || _primPath.IsEmpty()) { + return; + } + + UsdPrim prim = _stage->GetPrimAtPath(_primPath); + if (prim.IsValid()) { + auto bindingAPI = UsdShadeMaterialBindingAPI(prim); + if (bindingAPI) { + auto materialBinding = bindingAPI.GetDirectBinding(); + _materialPath = materialBinding.GetMaterialPath(); + if (!_materialPath.IsEmpty()) { + bindingAPI.UnbindDirectBinding(); + // TODO: Can we remove the BindingAPI at this point? + // Not easy to know for sure. + } + } + } +} +const std::string UnbindMaterialUndoableCommand::commandName("Unbind Material"); + +UsdUndoAssignNewMaterialCommand::UsdUndoAssignNewMaterialCommand( + const UsdSceneItem::Ptr& parentItem, + const std::string& nodeId) + : Ufe::InsertChildCommand() + , _parentPath(parentItem->path()) + , _nodeId(nodeId) +{ +} + +UsdUndoAssignNewMaterialCommand::~UsdUndoAssignNewMaterialCommand() { } + +UsdUndoAssignNewMaterialCommand::Ptr UsdUndoAssignNewMaterialCommand::create( + const UsdSceneItem::Ptr& parentItem, + const std::string& nodeId) +{ + // Changing the hierarchy of invalid items is not allowed. + if (!parentItem || !parentItem->prim().IsActive()) + return nullptr; + + return std::make_shared(parentItem, nodeId); +} + +Ufe::SceneItem::Ptr UsdUndoAssignNewMaterialCommand::insertedChild() const +{ + return _createShaderCmd ? _createShaderCmd->insertedChild() : nullptr; +} + +namespace { +// This class insures that either everything has succeeded, or makes sure to bring back to the +// initial state: +class UndoGuard +{ +public: + UndoGuard(Ufe::UndoableCommand* cmd) + : _this(cmd) + { + } + ~UndoGuard() + { + // Incomplete. Undo all stored tasks until we get back to original state. + if (_this) { + _this->undo(); + } + } + void markAsCompleted() { _this = nullptr; } + +private: + Ufe::UndoableCommand* _this = nullptr; +}; + +static const std::string kDefaultMaterialScopeName("mtl"); +} // namespace + +void UsdUndoAssignNewMaterialCommand::execute() +{ + UndoGuard guard(this); + // + // 1. Create the Scope "materials" if it does not exist: + // + auto parentItem + = std::dynamic_pointer_cast(Ufe::Hierarchy::createItem(_parentPath)); + Ufe::Path scopePath; + PXR_NS::UsdStageWeakPtr stage = getStage(parentItem->path()); + if (stage) { + auto stageHierarchy + = Ufe::Hierarchy::hierarchy(Ufe::Hierarchy::createItem(_parentPath.popSegment())); + if (stageHierarchy) { + for (auto&& child : stageHierarchy->children()) { + // Could be "mtl1" if there is already something named mtl which is not a scope. + if (child->nodeName().rfind(kDefaultMaterialScopeName, 0) == 0 + && child->nodeType() == "Scope") { + scopePath = child->path(); + break; + } + } + } + if (scopePath.empty()) { + _createScopeCmd = UsdUndoAddNewPrimCommand::create( + UsdSceneItem::create(MayaUsd::ufe::stagePath(stage), stage->GetPseudoRoot()), + kDefaultMaterialScopeName, + "Scope"); + _createScopeCmd->execute(); + scopePath = _createScopeCmd->newUfePath(); + // The code automatically appends a "1". We need to rename: + auto itemOps = Ufe::SceneItemOps::sceneItemOps(Ufe::Hierarchy::createItem(scopePath)); + auto rename = itemOps->renameItemCmd(Ufe::PathComponent(kDefaultMaterialScopeName)); + _renameScopeCmd = rename.undoableCommand; + scopePath = rename.item->path(); + } + } + if (scopePath.empty()) { + TF_RUNTIME_ERROR("Failed to create materials scope at stage root"); + return; + } + // + // 2. Create the Material if it does not exist: + // + PXR_NS::SdrRegistry& registry = PXR_NS::SdrRegistry::GetInstance(); + PXR_NS::SdrShaderNodeConstPtr shaderNodeDef + = registry.GetShaderNodeByIdentifier(TfToken(_nodeId)); + if (!shaderNodeDef) { + TF_RUNTIME_ERROR("Unknown shader identifier: %s", _nodeId.c_str()); + return; + } + if (shaderNodeDef->GetOutputNames().empty()) { + TF_RUNTIME_ERROR("Surface shader %s does not have any outputs", _nodeId.c_str()); + return; + } + auto scopeItem = std::dynamic_pointer_cast(Ufe::Hierarchy::createItem(scopePath)); + _createMaterialCmd = UsdUndoAddNewPrimCommand::create( + scopeItem, shaderNodeDef->GetFamily().GetString(), "Material"); + _createMaterialCmd->execute(); + if (!_createMaterialCmd->newPrim()) { + TF_RUNTIME_ERROR("Could not create material under %s", scopePath.string().c_str()); + return; + } + // + // 3. Create the Shader if it does not exist: + // + auto materialItem = std::dynamic_pointer_cast( + Ufe::Hierarchy::createItem(_createMaterialCmd->newUfePath())); + _createShaderCmd = UsdUndoCreateFromNodeDefCommand::create( + shaderNodeDef, materialItem, shaderNodeDef->GetFamily().GetString()); + _createShaderCmd->execute(); + if (!_createShaderCmd->insertedChild()) { + TF_RUNTIME_ERROR( + "Could not create %s shader under %s", + _nodeId.c_str(), + _createMaterialCmd->newUfePath().string().c_str()); + return; + } + // + // 4. Connect the Shader to the material: + // + if (!connectShaderToMaterial( + _createShaderCmd->insertedChild(), _createMaterialCmd->newPrim())) { + return; + } + // + // 5. Bind the material to the parent primitive: + // + _bindCmd = std::make_shared( + parentItem->prim(), materialItem->prim().GetPath()); + _bindCmd->execute(); + + // Successfully completed the task: + guard.markAsCompleted(); +} + +void UsdUndoAssignNewMaterialCommand::undo() +{ + // Reverse order of creation/execution: + if (_bindCmd) { + _bindCmd->undo(); + } + // We do not need to undo the connection of the shader to the surface output + // We do not need to undo the creation of the surface shader output + if (_createShaderCmd) { + _createShaderCmd->undo(); + } + if (_createMaterialCmd) { + _createMaterialCmd->undo(); + } + if (_createScopeCmd) { + _renameScopeCmd->undo(); + _createScopeCmd->undo(); + } +} + +void UsdUndoAssignNewMaterialCommand::redo() +{ + if (_createScopeCmd) { + _createScopeCmd->redo(); + _renameScopeCmd->redo(); + } + _createMaterialCmd->redo(); + _createShaderCmd->redo(); + connectShaderToMaterial(_createShaderCmd->insertedChild(), _createMaterialCmd->newPrim()); + _bindCmd->redo(); +} + +bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( + Ufe::SceneItem::Ptr shaderItem, + UsdPrim materialPrim) +{ + auto shaderUsdItem = std::dynamic_pointer_cast(shaderItem); + auto shaderPrim = UsdShadeShader(shaderUsdItem->prim()); + UsdShadeOutput materialOutput; + PXR_NS::SdrRegistry& registry = PXR_NS::SdrRegistry::GetInstance(); + PXR_NS::SdrShaderNodeConstPtr shaderNodeDef + = registry.GetShaderNodeByIdentifier(TfToken(_nodeId)); + SdrShaderPropertyConstPtr shaderOutputDef; + if (shaderNodeDef->GetSourceType() == "glslfx") { + materialOutput = UsdShadeMaterial(materialPrim).CreateSurfaceOutput(); + shaderOutputDef = shaderNodeDef->GetShaderOutput(TfToken("surface")); + } else { + if (shaderNodeDef->GetOutputNames().size() != 1) { + TF_RUNTIME_ERROR( + "Cannot resolve which output of shader %s should be connected to surface", + _nodeId.c_str()); + return false; + } + materialOutput + = UsdShadeMaterial(materialPrim).CreateSurfaceOutput(shaderNodeDef->GetSourceType()); + shaderOutputDef = shaderNodeDef->GetShaderOutput(shaderNodeDef->GetOutputNames()[0]); + } + UsdShadeOutput shaderOutput = shaderPrim.CreateOutput( + shaderOutputDef->GetName(), shaderOutputDef->GetTypeAsSdfType().first); + UsdShadeConnectableAPI::ConnectToSource(materialOutput, shaderOutput); + return true; +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h new file mode 100644 index 0000000000..c77d66859d --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h @@ -0,0 +1,127 @@ +// +// Copyright 2019 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief BindMaterialUndoableCommand +class MAYAUSD_CORE_PUBLIC BindMaterialUndoableCommand : public Ufe::UndoableCommand +{ +public: + static const std::string commandName; + + static PXR_NS::UsdPrim CompatiblePrim(const Ufe::SceneItem::Ptr& item); + + BindMaterialUndoableCommand(const PXR_NS::UsdPrim& prim, const PXR_NS::SdfPath& materialPath); + ~BindMaterialUndoableCommand() override; + + // Delete the copy/move constructors assignment operators. + BindMaterialUndoableCommand(const BindMaterialUndoableCommand&) = delete; + BindMaterialUndoableCommand& operator=(const BindMaterialUndoableCommand&) = delete; + BindMaterialUndoableCommand(BindMaterialUndoableCommand&&) = delete; + BindMaterialUndoableCommand& operator=(BindMaterialUndoableCommand&&) = delete; + + void undo() override; + void redo() override; + +private: + PXR_NS::UsdStageWeakPtr _stage; + PXR_NS::SdfPath _primPath; + PXR_NS::SdfPath _materialPath; + PXR_NS::SdfPath _previousMaterialPath; + bool _appliedBindingAPI = false; +}; + +//! \brief UnbindMaterialUndoableCommand +class MAYAUSD_CORE_PUBLIC UnbindMaterialUndoableCommand : public Ufe::UndoableCommand +{ +public: + static const std::string commandName; + + UnbindMaterialUndoableCommand(const PXR_NS::UsdPrim& prim); + ~UnbindMaterialUndoableCommand() override; + + // Delete the copy/move constructors assignment operators. + UnbindMaterialUndoableCommand(const UnbindMaterialUndoableCommand&) = delete; + UnbindMaterialUndoableCommand& operator=(const UnbindMaterialUndoableCommand&) = delete; + UnbindMaterialUndoableCommand(UnbindMaterialUndoableCommand&&) = delete; + UnbindMaterialUndoableCommand& operator=(UnbindMaterialUndoableCommand&&) = delete; + + void undo() override; + void redo() override; + +private: + PXR_NS::UsdStageWeakPtr _stage; + PXR_NS::SdfPath _primPath; + PXR_NS::SdfPath _materialPath; +}; + +//! \brief UsdUndoAssignNewMaterialCommand +class MAYAUSD_CORE_PUBLIC UsdUndoAssignNewMaterialCommand : public Ufe::InsertChildCommand +{ +public: + typedef std::shared_ptr Ptr; + + UsdUndoAssignNewMaterialCommand( + const UsdSceneItem::Ptr& parentItem, + const std::string& sdrShaderIdentifier); + ~UsdUndoAssignNewMaterialCommand() override; + + // Delete the copy/move constructors assignment operators. + UsdUndoAssignNewMaterialCommand(const UsdUndoAssignNewMaterialCommand&) = delete; + UsdUndoAssignNewMaterialCommand& operator=(const UsdUndoAssignNewMaterialCommand&) = delete; + UsdUndoAssignNewMaterialCommand(UsdUndoAssignNewMaterialCommand&&) = delete; + UsdUndoAssignNewMaterialCommand& operator=(UsdUndoAssignNewMaterialCommand&&) = delete; + + //! Create a UsdUndoAssignNewMaterialCommand that creates a new material based on \p + //! sdrShaderIdentifier and assigns it to \p parentItem + static UsdUndoAssignNewMaterialCommand::Ptr + create(const UsdSceneItem::Ptr& parentItem, const std::string& sdrShaderIdentifier); + + Ufe::SceneItem::Ptr insertedChild() const override; + + void execute() override; + void undo() override; + void redo() override; + +private: + bool connectShaderToMaterial(Ufe::SceneItem::Ptr shaderItem, PXR_NS::UsdPrim materialPrim); + + const Ufe::Path _parentPath; + const std::string _nodeId; + + std::shared_ptr _createScopeCmd; + UndoableCommand::Ptr _renameScopeCmd; + std::shared_ptr _createMaterialCmd; + std::shared_ptr _createShaderCmd; + std::shared_ptr _bindCmd; + +}; // UsdUndoAssignNewMaterialCommand + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py index 6ba3f5435e..18a8fd461d 100644 --- a/test/lib/ufe/testContextOps.py +++ b/test/lib/ufe/testContextOps.py @@ -408,6 +408,116 @@ def testMaterialBinding(self): cmds.redo() self.assertTrue(capsuleBindAPI.GetDirectBinding().GetMaterialPath().isEmpty) + @unittest.skipUnless(Usd.GetVersion() >= (0, 21, 8), 'Requires CanApplySchema from USD') + def testMaterialCreation(self): + """This test builds a material using contextOps capabilities.""" + cmds.file(new=True, force=True) + + # Create a proxy shape with empty stage to start with. + import mayaUsd_createStageWithNewLayer + proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + + # Create a ContextOps interface for the proxy shape. + proxyPathSegment = mayaUtils.createUfePathSegment(proxyShape) + proxyShapePath = ufe.Path([proxyPathSegment]) + proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) + contextOps = ufe.ContextOps.contextOps(proxyShapeItem) + + cmd = contextOps.doOpCmd(['Add New Prim', 'Capsule']) + ufeCmd.execute(cmd) + + rootHier = ufe.Hierarchy.hierarchy(proxyShapeItem) + self.assertTrue(rootHier.hasChildren()) + self.assertEqual(len(rootHier.children()), 1) + + capsuleItem = rootHier.children()[-1] + contextOps = ufe.ContextOps.contextOps(capsuleItem) + + capsulePrim = usdUtils.getPrimFromSceneItem(capsuleItem) + self.assertFalse(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + + cmdPS = contextOps.doOpCmd(['Assign New Material', 'USD', 'UsdPreviewSurface']) + self.assertIsNotNone(cmdPS) + ufeCmd.execute(cmdPS) + + # Complex command. We should now have a fully working preview surface material bound to + # the capsule prim: + def checkMaterial(self, rootHier, numMat, idx, matType, context, shaderOutputName): + self.assertTrue(rootHier.hasChildren()) + self.assertEqual(len(rootHier.children()), 2) + + def checkItem(self, item, type, path): + self.assertEqual(item.nodeType(), type) + prim = usdUtils.getPrimFromSceneItem(item) + self.assertEqual(prim.GetPath(), Sdf.Path(path)) + + scopeItem = rootHier.children()[-1] + checkItem(self, scopeItem, "Scope", "/mtl") + + scopeHier = ufe.Hierarchy.hierarchy(scopeItem) + self.assertTrue(scopeHier.hasChildren()) + self.assertEqual(len(scopeHier.children()), numMat) + materialItem = scopeHier.children()[idx] + checkItem(self, materialItem, "Material", "/mtl/{0}1".format(matType)) + + # Binding and selection are always on the last item: + if (numMat - 1 == idx): + self.assertTrue(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + self.assertEqual(UsdShade.MaterialBindingAPI(capsulePrim).GetDirectBinding().GetMaterialPath(), + Sdf.Path("/mtl/{0}1".format(matType))) + selection = ufe.GlobalSelection.get() + self.assertEqual(len(selection), 1) + self.assertEqual(selection.front().nodeName(), "{0}1".format(matType)) + self.assertEqual(selection.front().nodeType(), "Shader") + + + hier = ufe.Hierarchy.hierarchy(materialItem) + self.assertTrue(hier.hasChildren()) + self.assertEqual(len(hier.children()), 1) + shaderItem = hier.children()[0] + checkItem(self, shaderItem, "Shader", "/mtl/{0}1/{0}1".format(matType)) + + materialPrim = UsdShade.Material(usdUtils.getPrimFromSceneItem(materialItem)) + self.assertTrue(materialPrim) + + surfaceOutput = materialPrim.GetSurfaceOutput(context) + self.assertTrue(surfaceOutput) + + sourceInfos, invalidPaths = surfaceOutput.GetConnectedSources() + self.assertEqual(len(sourceInfos), 1) + self.assertEqual(sourceInfos[0].source.GetPath(), Sdf.Path("/mtl/{0}1/{0}1".format(matType))) + self.assertEqual(sourceInfos[0].sourceName, shaderOutputName) + + checkMaterial(self, rootHier, 1, 0, "UsdPreviewSurface", "", "surface") + + cmdSS = contextOps.doOpCmd(['Assign New Material', 'MaterialX', 'ND_standard_surface_surfaceshader']) + self.assertIsNotNone(cmdSS) + ufeCmd.execute(cmdSS) + + checkMaterial(self, rootHier, 2, 0, "UsdPreviewSurface", "", "surface") + checkMaterial(self, rootHier, 2, 1, "standard_surface", "mtlx", "out") + + cmds.undo() + + checkMaterial(self, rootHier, 1, 0, "UsdPreviewSurface", "", "surface") + + cmds.redo() + + checkMaterial(self, rootHier, 2, 0, "UsdPreviewSurface", "", "surface") + checkMaterial(self, rootHier, 2, 1, "standard_surface", "mtlx", "out") + + cmds.undo() + cmds.undo() + + # Not even a mtl scope: + self.assertEqual(len(rootHier.children()), 1) + self.assertFalse(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + + cmds.redo() + + checkMaterial(self, rootHier, 1, 0, "UsdPreviewSurface", "", "surface") + + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4010', 'Test only available in UFE preview version 0.4.10 and greater') @unittest.skipUnless(Usd.GetVersion() >= (0, 21, 8), 'Requires CanApplySchema from USD') def testMaterialBindingWithNodeDefHandler(self): From 2e1cb2c9a89bd2e3825c6807586593e24c4f23d0 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Thu, 7 Jul 2022 23:15:53 -0400 Subject: [PATCH 3/6] Fix builds on older Ufe versions --- lib/mayaUsd/ufe/CMakeLists.txt | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index a92fb0defe..e04e76008a 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -109,6 +109,12 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderAttributeDef.cpp UsdUndoCreateFromNodeDefCommand.cpp ) + if(PXR_VERSION GREATER_EQUAL 2108) + target_sources(${PROJECT_NAME} + PRIVATE + UsdUndoMaterialCommands.cpp + ) + endif() endif() if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4013) @@ -128,13 +134,6 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) endif() endif() -if(PXR_VERSION GREATER_EQUAL 2108) - target_sources(${PROJECT_NAME} - PRIVATE - UsdUndoMaterialCommands.cpp - ) -endif() - set(HEADERS Global.h ProxyShapeHandler.h @@ -234,6 +233,11 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderAttributeDef.h UsdUndoCreateFromNodeDefCommand.h ) + if(PXR_VERSION GREATER_EQUAL 2108) + list(APPEND HEADERS + UsdUndoMaterialCommands.h + ) + endif() endif() if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4013) @@ -251,12 +255,6 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) endif() endif() -if(PXR_VERSION GREATER_EQUAL 2108) - list(APPEND HEADERS - UsdUndoMaterialCommands.h - ) -endif() - # ----------------------------------------------------------------------------- # promote headers # ----------------------------------------------------------------------------- From 3d2862a4535f55ae3d80095e6122703fd113b000 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 8 Jul 2022 10:02:18 -0400 Subject: [PATCH 4/6] Properly check for UFE versions Tested against Maya 2022 --- lib/mayaUsd/ufe/CMakeLists.txt | 24 +++++++++++---------- lib/mayaUsd/ufe/UsdContextOps.cpp | 15 +++++++++++++ lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp | 9 ++------ lib/mayaUsd/ufe/UsdUndoMaterialCommands.h | 4 ++++ test/lib/ufe/testContextOps.py | 1 + 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index e04e76008a..a92fb0defe 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -109,12 +109,6 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderAttributeDef.cpp UsdUndoCreateFromNodeDefCommand.cpp ) - if(PXR_VERSION GREATER_EQUAL 2108) - target_sources(${PROJECT_NAME} - PRIVATE - UsdUndoMaterialCommands.cpp - ) - endif() endif() if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4013) @@ -134,6 +128,13 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) endif() endif() +if(PXR_VERSION GREATER_EQUAL 2108) + target_sources(${PROJECT_NAME} + PRIVATE + UsdUndoMaterialCommands.cpp + ) +endif() + set(HEADERS Global.h ProxyShapeHandler.h @@ -233,11 +234,6 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderAttributeDef.h UsdUndoCreateFromNodeDefCommand.h ) - if(PXR_VERSION GREATER_EQUAL 2108) - list(APPEND HEADERS - UsdUndoMaterialCommands.h - ) - endif() endif() if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4013) @@ -255,6 +251,12 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) endif() endif() +if(PXR_VERSION GREATER_EQUAL 2108) + list(APPEND HEADERS + UsdUndoMaterialCommands.h + ) +endif() + # ----------------------------------------------------------------------------- # promote headers # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index d6d4466b89..7ff2e82a37 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,7 @@ static constexpr char kAddMayaReferenceLabel[] = "Add Maya Reference..."; #if PXR_VERSION >= 2108 static constexpr char kBindMaterialToSelectionItem[] = "Assign Material to Selection"; static constexpr char kBindMaterialToSelectionLabel[] = "Assign Material to Selection"; +#if UFE_PREVIEW_VERSION_NUM >= 4010 static constexpr char kAssignNewMaterialItem[] = "Assign New Material"; static constexpr char kAssignNewMaterialLabel[] = "Assign New Material"; static constexpr char kAssignNewUsdMaterialItem[] = "USD Material"; @@ -147,6 +149,7 @@ static constexpr char kAssignNewUsdPreviewSurfaceMaterialLabel[] = "Usd Preview static constexpr char kAssignNewAIStandardSurfaceMaterialItem[] = "arnold:standard_surface"; static constexpr char kAssignNewAIStandardSurfaceMaterialLabel[] = "AI Standard Surface"; #endif +#endif #if PXR_VERSION >= 2008 static constexpr char kAllRegisteredTypesItem[] = "All Registered"; @@ -245,6 +248,7 @@ const MxShaderMenuEntryVec& getMaterialXSurfaceShaders() return mxSurfaceShaders; } +#ifdef UFE_V3_FEATURES_AVAILABLE //! \brief Create an Item and select it: class BaseCreateAndSelectUndoableCommand : public Ufe::UndoableCommand { @@ -322,6 +326,7 @@ class InsertChildAndSelectCommand : public BaseCreateAndSelectUndoableCommand } } }; +#endif //! \brief Undoable command for loading a USD prim. class LoadUnloadBaseUndoableCommand : public Ufe::UndoableCommand @@ -963,10 +968,12 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& Ufe::ContextItem::kHasChildren); } } +#if UFE_PREVIEW_VERSION_NUM >= 4010 items.emplace_back( kAssignNewMaterialItem, kAssignNewMaterialLabel, Ufe::ContextItem::kHasChildren); +#endif } if (fItem->prim().HasAPI()) { UsdShadeMaterialBindingAPI bindingAPI(fItem->prim()); @@ -1100,6 +1107,7 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& } } } +#if UFE_PREVIEW_VERSION_NUM >= 4010 } else if (itemPath.size() == 1u && itemPath[0] == kAssignNewMaterialItem) { items.emplace_back( kAssignNewUsdMaterialItem, @@ -1125,6 +1133,7 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& } else if (itemPath.size() == 2u && itemPath[1] == kAssignNewArnoldMaterialItem) { items.emplace_back( kAssignNewAIStandardSurfaceMaterialItem, kAssignNewAIStandardSurfaceMaterialLabel); +#endif } #endif } // Top-level items @@ -1179,8 +1188,12 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) } // At this point we know the last item in the itemPath is the prim type to create auto primType = itemPath[itemPath.size() - 1]; +#ifdef UFE_V3_FEATURES_AVAILABLE return std::make_shared( UsdUndoAddNewPrimCommand::create(fItem, primType, primType)); +#else + return UsdUndoAddNewPrimCommand::create(fItem, primType, primType); +#endif #ifdef WANT_QT_BUILD // When building without Qt there is no LayerEditor } else if (itemPath[0] == kUSDLayerEditorItem) { @@ -1252,11 +1265,13 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return compositeCmd; } else if (itemPath[0] == UnbindMaterialUndoableCommand::commandName) { return std::make_shared(fItem->prim()); +#if UFE_PREVIEW_VERSION_NUM >= 4010 } else if (itemPath.size() == 3u && itemPath[0] == kAssignNewMaterialItem) { if (fItem) { return std::make_shared( UsdUndoAssignNewMaterialCommand::create(fItem, itemPath[2])); } +#endif } #endif return nullptr; diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp index c1298f3aeb..3819f7c933 100644 --- a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp @@ -15,18 +15,12 @@ // #include "UsdUndoMaterialCommands.h" -#include -#include #include #include #include #include -#include -#include -#include -#include #include #include @@ -158,6 +152,7 @@ void UnbindMaterialUndoableCommand::redo() } const std::string UnbindMaterialUndoableCommand::commandName("Unbind Material"); +#if (UFE_PREVIEW_VERSION_NUM >= 4010) UsdUndoAssignNewMaterialCommand::UsdUndoAssignNewMaterialCommand( const UsdSceneItem::Ptr& parentItem, const std::string& nodeId) @@ -369,6 +364,6 @@ bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( UsdShadeConnectableAPI::ConnectToSource(materialOutput, shaderOutput); return true; } - +#endif } // namespace ufe } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h index c77d66859d..de723d6e7b 100644 --- a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h @@ -17,8 +17,10 @@ #include #include +#if (UFE_PREVIEW_VERSION_NUM >= 4010) #include #include +#endif #include @@ -81,6 +83,7 @@ class MAYAUSD_CORE_PUBLIC UnbindMaterialUndoableCommand : public Ufe::UndoableCo PXR_NS::SdfPath _materialPath; }; +#if (UFE_PREVIEW_VERSION_NUM >= 4010) //! \brief UsdUndoAssignNewMaterialCommand class MAYAUSD_CORE_PUBLIC UsdUndoAssignNewMaterialCommand : public Ufe::InsertChildCommand { @@ -122,6 +125,7 @@ class MAYAUSD_CORE_PUBLIC UsdUndoAssignNewMaterialCommand : public Ufe::InsertCh std::shared_ptr _bindCmd; }; // UsdUndoAssignNewMaterialCommand +#endif } // namespace ufe } // namespace MAYAUSD_NS_DEF diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py index 18a8fd461d..9bda0185a1 100644 --- a/test/lib/ufe/testContextOps.py +++ b/test/lib/ufe/testContextOps.py @@ -408,6 +408,7 @@ def testMaterialBinding(self): cmds.redo() self.assertTrue(capsuleBindAPI.GetDirectBinding().GetMaterialPath().isEmpty) + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4010', 'Test only available in UFE preview version 0.4.10 and greater') @unittest.skipUnless(Usd.GetVersion() >= (0, 21, 8), 'Requires CanApplySchema from USD') def testMaterialCreation(self): """This test builds a material using contextOps capabilities.""" From 5667d0bab9529e2129f150f3215e8e9bbdea372d Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 8 Jul 2022 10:53:53 -0400 Subject: [PATCH 5/6] Rework from review comments. --- lib/mayaUsd/ufe/UsdContextOps.cpp | 17 +++++---- lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp | 39 ++++++++++++++++----- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index 7ff2e82a37..433ac12521 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -195,6 +195,7 @@ struct WaitCursor ~WaitCursor() { MGlobal::executeCommand("waitCursor -state 0"); } }; +#if UFE_PREVIEW_VERSION_NUM >= 4010 //! \brief This check has a 3 seconds slowdown to load all Sdr nodes in the registry. We do it in /// advance in order to not have this 3 seconds delay when the "Assign New Material" submenu /// is built for the first time. @@ -217,7 +218,7 @@ struct MxShaderMenuEntry , _identifier(identifier) { } - std::string _label; + const std::string _label; const std::string& _identifier; }; typedef std::vector MxShaderMenuEntryVec; @@ -225,7 +226,8 @@ typedef std::vector MxShaderMenuEntryVec; const MxShaderMenuEntryVec& getMaterialXSurfaceShaders() { static MxShaderMenuEntryVec mxSurfaceShaders; - if (mxSurfaceShaders.empty()) { + static bool initialized = false; + if (!initialized) { auto& sdrRegistry = PXR_NS::SdrRegistry::GetInstance(); // Here is a list of nodes we know work fine as starting materials for the contextual menu. // We might add discovery code later, but this discovery code will have the difficult task @@ -233,20 +235,23 @@ const MxShaderMenuEntryVec& getMaterialXSurfaceShaders() // - utility nodes like ND_add_surfaceshader // - basic building blocks like ND_thin_surface // - shaders that exist only as pure definitions like ND_disney_bsdf_2015_surface - const std::vector vettedSurfaces = { "ND_standard_surface_surfaceshader", - "ND_gltf_pbr_surfaceshader", - "ND_UsdPreviewSurface_surfaceshader" }; + static const std::vector vettedSurfaces + = { "ND_standard_surface_surfaceshader", + "ND_gltf_pbr_surfaceshader", + "ND_UsdPreviewSurface_surfaceshader" }; for (auto&& identifier : vettedSurfaces) { auto shaderDef = sdrRegistry.GetShaderNodeByIdentifier(TfToken(identifier)); if (!shaderDef) { continue; } - std::string label = UsdMayaUtil::prettifyName(shaderDef->GetFamily().GetString()); + const std::string label = UsdMayaUtil::prettifyName(shaderDef->GetFamily().GetString()); mxSurfaceShaders.emplace_back(label, shaderDef->GetIdentifier().GetString()); } + initialized = true; } return mxSurfaceShaders; } +#endif #ifdef UFE_V3_FEATURES_AVAILABLE //! \brief Create an Item and select it: diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp index 3819f7c933..4fe216971f 100644 --- a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp @@ -186,10 +186,11 @@ namespace { class UndoGuard { public: - UndoGuard(Ufe::UndoableCommand* cmd) + explicit UndoGuard(Ufe::UndoableCommand* cmd) : _this(cmd) { } + ~UndoGuard() { // Incomplete. Undo all stored tasks until we get back to original state. @@ -244,7 +245,7 @@ void UsdUndoAssignNewMaterialCommand::execute() } } if (scopePath.empty()) { - TF_RUNTIME_ERROR("Failed to create materials scope at stage root"); + // The _createScopeCmd and/or _renameScopeCmd will have emitted errors. return; } // @@ -266,7 +267,7 @@ void UsdUndoAssignNewMaterialCommand::execute() scopeItem, shaderNodeDef->GetFamily().GetString(), "Material"); _createMaterialCmd->execute(); if (!_createMaterialCmd->newPrim()) { - TF_RUNTIME_ERROR("Could not create material under %s", scopePath.string().c_str()); + // The _createMaterialCmd will have emitted errors. return; } // @@ -278,10 +279,7 @@ void UsdUndoAssignNewMaterialCommand::execute() shaderNodeDef, materialItem, shaderNodeDef->GetFamily().GetString()); _createShaderCmd->execute(); if (!_createShaderCmd->insertedChild()) { - TF_RUNTIME_ERROR( - "Could not create %s shader under %s", - _nodeId.c_str(), - _createMaterialCmd->newUfePath().string().c_str()); + // The _createShaderCmd will have emitted errors. return; } // @@ -324,14 +322,39 @@ void UsdUndoAssignNewMaterialCommand::undo() void UsdUndoAssignNewMaterialCommand::redo() { + if (!_bindCmd) { + // Initial execute() call failed to complete. + return; + } + // If redo fails, then there is a bigger problem to fix because the scene state is not what + // it should be before redo is called. + UndoGuard guard(this); + + // Each subcommand is responsible for emitting error messages on redo. We will not tag extra + // messages if a subcommand fails. if (_createScopeCmd) { _createScopeCmd->redo(); + if (_createScopeCmd->newUfePath().empty()) { + return; + } _renameScopeCmd->redo(); } _createMaterialCmd->redo(); + if (_createMaterialCmd->newUfePath().empty()) { + return; + } _createShaderCmd->redo(); - connectShaderToMaterial(_createShaderCmd->insertedChild(), _createMaterialCmd->newPrim()); + if (!_createShaderCmd->insertedChild()) { + return; + } + if (!connectShaderToMaterial( + _createShaderCmd->insertedChild(), _createMaterialCmd->newPrim())) { + return; + } _bindCmd->redo(); + + // Successfully completed the task: + guard.markAsCompleted(); } bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( From e82bc408feef1b8223402b33ecb10d6b77feddcf Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 8 Jul 2022 13:28:17 -0400 Subject: [PATCH 6/6] Use CompositeCmd everywhere --- lib/mayaUsd/ufe/UsdContextOps.cpp | 57 ++----- lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp | 151 +++++++------------ lib/mayaUsd/ufe/UsdUndoMaterialCommands.h | 10 +- test/lib/mayaUsd/nodes/testProxyShapeBase.py | 6 +- 4 files changed, 80 insertions(+), 144 deletions(-) diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index 433ac12521..543e99a135 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -254,80 +254,51 @@ const MxShaderMenuEntryVec& getMaterialXSurfaceShaders() #endif #ifdef UFE_V3_FEATURES_AVAILABLE -//! \brief Create an Item and select it: -class BaseCreateAndSelectUndoableCommand : public Ufe::UndoableCommand -{ -public: - BaseCreateAndSelectUndoableCommand(const Ufe::UndoableCommand::Ptr& creationCmd) - : _creationCmd(creationCmd) - { - } - - void redo() override - { - if (_selectionCmd) { - _creationCmd->redo(); - _selectionCmd->redo(); - } - } - - void undo() override - { - if (_selectionCmd) { - _selectionCmd->undo(); - _creationCmd->undo(); - } - } - -protected: - Ufe::UndoableCommand::Ptr _creationCmd; - Ufe::UndoableCommand::Ptr _selectionCmd; -}; - //! \brief Create a Prim and select it: -class UsdUndoAddNewPrimAndSelectCommand : public BaseCreateAndSelectUndoableCommand +class UsdUndoAddNewPrimAndSelectCommand : public Ufe::CompositeUndoableCommand { public: UsdUndoAddNewPrimAndSelectCommand( const MAYAUSD_NS::ufe::UsdUndoAddNewPrimCommand::Ptr& creationCmd) - : BaseCreateAndSelectUndoableCommand(creationCmd) + : Ufe::CompositeUndoableCommand({ creationCmd }) { } void execute() override { - _creationCmd->execute(); - auto addPrimCmd - = std::dynamic_pointer_cast(_creationCmd); + auto addPrimCmd = std::dynamic_pointer_cast( + cmdsList().front()); + addPrimCmd->execute(); // Create the selection command only if the creation succeeded: if (!addPrimCmd->newUfePath().empty()) { Ufe::Selection newSelection; newSelection.append(Ufe::Hierarchy::createItem(addPrimCmd->newUfePath())); - _selectionCmd = Ufe::SelectionReplaceWith::createAndExecute( - Ufe::GlobalSelection::get(), newSelection); + append(Ufe::SelectionReplaceWith::createAndExecute( + Ufe::GlobalSelection::get(), newSelection)); } } }; //! \brief Create a working Material and select it: -class InsertChildAndSelectCommand : public BaseCreateAndSelectUndoableCommand +class InsertChildAndSelectCommand : public Ufe::CompositeUndoableCommand { public: InsertChildAndSelectCommand(const Ufe::InsertChildCommand::Ptr& creationCmd) - : BaseCreateAndSelectUndoableCommand(creationCmd) + : Ufe::CompositeUndoableCommand({ creationCmd }) { } void execute() override { - _creationCmd->execute(); - auto insertChildCmd = std::dynamic_pointer_cast(_creationCmd); + auto insertChildCmd + = std::dynamic_pointer_cast(cmdsList().front()); + insertChildCmd->execute(); // Create the selection command only if the creation succeeded: if (insertChildCmd->insertedChild()) { Ufe::Selection newSelection; newSelection.append(insertChildCmd->insertedChild()); - _selectionCmd = Ufe::SelectionReplaceWith::createAndExecute( - Ufe::GlobalSelection::get(), newSelection); + append(Ufe::SelectionReplaceWith::createAndExecute( + Ufe::GlobalSelection::get(), newSelection)); } } }; diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp index 4fe216971f..6579dc60d9 100644 --- a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.cpp @@ -159,6 +159,7 @@ UsdUndoAssignNewMaterialCommand::UsdUndoAssignNewMaterialCommand( : Ufe::InsertChildCommand() , _parentPath(parentItem->path()) , _nodeId(nodeId) + , _cmds(std::make_shared()) { } @@ -177,39 +178,22 @@ UsdUndoAssignNewMaterialCommand::Ptr UsdUndoAssignNewMaterialCommand::create( Ufe::SceneItem::Ptr UsdUndoAssignNewMaterialCommand::insertedChild() const { - return _createShaderCmd ? _createShaderCmd->insertedChild() : nullptr; + if (_cmds) { + auto cmdsIt = _cmds->cmdsList().begin(); + std::advance(cmdsIt, _createMaterialCmdIdx + 1); + auto addShaderCmd = std::dynamic_pointer_cast(*cmdsIt); + return addShaderCmd->insertedChild(); + } + return {}; } namespace { -// This class insures that either everything has succeeded, or makes sure to bring back to the -// initial state: -class UndoGuard -{ -public: - explicit UndoGuard(Ufe::UndoableCommand* cmd) - : _this(cmd) - { - } - - ~UndoGuard() - { - // Incomplete. Undo all stored tasks until we get back to original state. - if (_this) { - _this->undo(); - } - } - void markAsCompleted() { _this = nullptr; } - -private: - Ufe::UndoableCommand* _this = nullptr; -}; - +// We will not use the value of UsdUtilsGetMaterialsScopeName() for the material scope. static const std::string kDefaultMaterialScopeName("mtl"); } // namespace void UsdUndoAssignNewMaterialCommand::execute() { - UndoGuard guard(this); // // 1. Create the Scope "materials" if it does not exist: // @@ -231,21 +215,23 @@ void UsdUndoAssignNewMaterialCommand::execute() } } if (scopePath.empty()) { - _createScopeCmd = UsdUndoAddNewPrimCommand::create( + auto createScopeCmd = UsdUndoAddNewPrimCommand::create( UsdSceneItem::create(MayaUsd::ufe::stagePath(stage), stage->GetPseudoRoot()), kDefaultMaterialScopeName, "Scope"); - _createScopeCmd->execute(); - scopePath = _createScopeCmd->newUfePath(); + createScopeCmd->execute(); + _cmds->append(createScopeCmd); + scopePath = createScopeCmd->newUfePath(); // The code automatically appends a "1". We need to rename: auto itemOps = Ufe::SceneItemOps::sceneItemOps(Ufe::Hierarchy::createItem(scopePath)); auto rename = itemOps->renameItemCmd(Ufe::PathComponent(kDefaultMaterialScopeName)); - _renameScopeCmd = rename.undoableCommand; + _cmds->append(rename.undoableCommand); scopePath = rename.item->path(); } } if (scopePath.empty()) { // The _createScopeCmd and/or _renameScopeCmd will have emitted errors. + markAsFailed(); return; } // @@ -256,108 +242,79 @@ void UsdUndoAssignNewMaterialCommand::execute() = registry.GetShaderNodeByIdentifier(TfToken(_nodeId)); if (!shaderNodeDef) { TF_RUNTIME_ERROR("Unknown shader identifier: %s", _nodeId.c_str()); + markAsFailed(); return; } if (shaderNodeDef->GetOutputNames().empty()) { TF_RUNTIME_ERROR("Surface shader %s does not have any outputs", _nodeId.c_str()); + markAsFailed(); return; } auto scopeItem = std::dynamic_pointer_cast(Ufe::Hierarchy::createItem(scopePath)); - _createMaterialCmd = UsdUndoAddNewPrimCommand::create( + auto createMaterialCmd = UsdUndoAddNewPrimCommand::create( scopeItem, shaderNodeDef->GetFamily().GetString(), "Material"); - _createMaterialCmd->execute(); - if (!_createMaterialCmd->newPrim()) { + createMaterialCmd->execute(); + _createMaterialCmdIdx = _cmds->cmdsList().size(); + _cmds->append(createMaterialCmd); + if (!createMaterialCmd->newPrim()) { // The _createMaterialCmd will have emitted errors. + markAsFailed(); return; } // // 3. Create the Shader if it does not exist: // auto materialItem = std::dynamic_pointer_cast( - Ufe::Hierarchy::createItem(_createMaterialCmd->newUfePath())); - _createShaderCmd = UsdUndoCreateFromNodeDefCommand::create( + Ufe::Hierarchy::createItem(createMaterialCmd->newUfePath())); + auto createShaderCmd = UsdUndoCreateFromNodeDefCommand::create( shaderNodeDef, materialItem, shaderNodeDef->GetFamily().GetString()); - _createShaderCmd->execute(); - if (!_createShaderCmd->insertedChild()) { + createShaderCmd->execute(); + _cmds->append(createShaderCmd); + if (!createShaderCmd->insertedChild()) { // The _createShaderCmd will have emitted errors. + markAsFailed(); return; } // // 4. Connect the Shader to the material: // - if (!connectShaderToMaterial( - _createShaderCmd->insertedChild(), _createMaterialCmd->newPrim())) { + connectShaderToMaterial(createShaderCmd->insertedChild(), createMaterialCmd->newPrim()); + if (!_cmds) { + // connect has failed. return; } + // // 5. Bind the material to the parent primitive: // - _bindCmd = std::make_shared( + auto bindCmd = std::make_shared( parentItem->prim(), materialItem->prim().GetPath()); - _bindCmd->execute(); - - // Successfully completed the task: - guard.markAsCompleted(); + bindCmd->execute(); + _cmds->append(bindCmd); } void UsdUndoAssignNewMaterialCommand::undo() { - // Reverse order of creation/execution: - if (_bindCmd) { - _bindCmd->undo(); - } - // We do not need to undo the connection of the shader to the surface output - // We do not need to undo the creation of the surface shader output - if (_createShaderCmd) { - _createShaderCmd->undo(); - } - if (_createMaterialCmd) { - _createMaterialCmd->undo(); - } - if (_createScopeCmd) { - _renameScopeCmd->undo(); - _createScopeCmd->undo(); + if (_cmds) { + _cmds->undo(); } } void UsdUndoAssignNewMaterialCommand::redo() { - if (!_bindCmd) { - // Initial execute() call failed to complete. - return; + if (_cmds) { + _cmds->redo(); + + auto cmdsIt = _cmds->cmdsList().begin(); + std::advance(cmdsIt, _createMaterialCmdIdx); + auto addMaterialCmd + = std::dynamic_pointer_cast(*cmdsIt++); + auto addShaderCmd = std::dynamic_pointer_cast(*cmdsIt); + connectShaderToMaterial(addShaderCmd->insertedChild(), addMaterialCmd->newPrim()); } - // If redo fails, then there is a bigger problem to fix because the scene state is not what - // it should be before redo is called. - UndoGuard guard(this); - - // Each subcommand is responsible for emitting error messages on redo. We will not tag extra - // messages if a subcommand fails. - if (_createScopeCmd) { - _createScopeCmd->redo(); - if (_createScopeCmd->newUfePath().empty()) { - return; - } - _renameScopeCmd->redo(); - } - _createMaterialCmd->redo(); - if (_createMaterialCmd->newUfePath().empty()) { - return; - } - _createShaderCmd->redo(); - if (!_createShaderCmd->insertedChild()) { - return; - } - if (!connectShaderToMaterial( - _createShaderCmd->insertedChild(), _createMaterialCmd->newPrim())) { - return; - } - _bindCmd->redo(); - - // Successfully completed the task: - guard.markAsCompleted(); } -bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( +void UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( Ufe::SceneItem::Ptr shaderItem, UsdPrim materialPrim) { @@ -376,7 +333,8 @@ bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( TF_RUNTIME_ERROR( "Cannot resolve which output of shader %s should be connected to surface", _nodeId.c_str()); - return false; + markAsFailed(); + return; } materialOutput = UsdShadeMaterial(materialPrim).CreateSurfaceOutput(shaderNodeDef->GetSourceType()); @@ -385,8 +343,15 @@ bool UsdUndoAssignNewMaterialCommand::connectShaderToMaterial( UsdShadeOutput shaderOutput = shaderPrim.CreateOutput( shaderOutputDef->GetName(), shaderOutputDef->GetTypeAsSdfType().first); UsdShadeConnectableAPI::ConnectToSource(materialOutput, shaderOutput); - return true; + return; } + +void UsdUndoAssignNewMaterialCommand::markAsFailed() +{ + _cmds->undo(); + _cmds.reset(); +} + #endif } // namespace ufe } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h index de723d6e7b..0cd92f6323 100644 --- a/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h +++ b/lib/mayaUsd/ufe/UsdUndoMaterialCommands.h @@ -113,16 +113,14 @@ class MAYAUSD_CORE_PUBLIC UsdUndoAssignNewMaterialCommand : public Ufe::InsertCh void redo() override; private: - bool connectShaderToMaterial(Ufe::SceneItem::Ptr shaderItem, PXR_NS::UsdPrim materialPrim); + void connectShaderToMaterial(Ufe::SceneItem::Ptr shaderItem, PXR_NS::UsdPrim materialPrim); + void markAsFailed(); const Ufe::Path _parentPath; const std::string _nodeId; - std::shared_ptr _createScopeCmd; - UndoableCommand::Ptr _renameScopeCmd; - std::shared_ptr _createMaterialCmd; - std::shared_ptr _createShaderCmd; - std::shared_ptr _bindCmd; + size_t _createMaterialCmdIdx = -1; + std::shared_ptr _cmds; }; // UsdUndoAssignNewMaterialCommand #endif diff --git a/test/lib/mayaUsd/nodes/testProxyShapeBase.py b/test/lib/mayaUsd/nodes/testProxyShapeBase.py index 3a3df7bb1d..c5ce65d800 100644 --- a/test/lib/mayaUsd/nodes/testProxyShapeBase.py +++ b/test/lib/mayaUsd/nodes/testProxyShapeBase.py @@ -79,8 +79,10 @@ def testDuplicateProxyStageAnonymous(self): self.assertEqual(True, "-session" in stage.GetSessionLayer().identifier) self.assertEqual(True, "anonymousLayer1" in stage.GetRootLayer().identifier) - # add proxyShapeItem to selection list - ufe.GlobalSelection.get().append(proxyShapeItem) + # Select proxyShapeItem + proxySelection = ufe.Selection() + proxySelection.append(proxyShapeItem) + ufe.GlobalSelection.get().replaceWith(proxySelection) # duplicate the proxyShape. cmds.duplicate()