diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index 979b0c601d..e9e4214390 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -94,6 +94,14 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderNodeDefHandler.cpp ) endif() + + if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4010) + target_sources(${PROJECT_NAME} + PRIVATE + UsdShaderAttributeDef.cpp + UsdUndoCreateFromNodeDefCommand.cpp + ) + endif() endif() set(HEADERS @@ -186,6 +194,13 @@ if(CMAKE_UFE_V4_FEATURES_AVAILABLE) UsdShaderNodeDefHandler.h ) endif() + + if (${UFE_PREVIEW_VERSION_NUM} GREATER_EQUAL 4010) + list(APPEND HEADERS + UsdShaderAttributeDef.h + UsdUndoCreateFromNodeDefCommand.h + ) + endif() endif() # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/ufe/UsdShaderAttributeDef.cpp b/lib/mayaUsd/ufe/UsdShaderAttributeDef.cpp new file mode 100644 index 0000000000..fde1352097 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdShaderAttributeDef.cpp @@ -0,0 +1,96 @@ +// +// Copyright 2022 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 "UsdShaderAttributeDef.h" + +#include "Global.h" +#include "Utils.h" + +#include +#include +#include + +#include +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +PXR_NAMESPACE_USING_DIRECTIVE + +UsdShaderAttributeDef::UsdShaderAttributeDef( + const PXR_NS::SdrShaderPropertyConstPtr& shaderAttributeDef) + : Ufe::AttributeDef() + , fShaderAttributeDef(shaderAttributeDef) +{ + if (!TF_VERIFY(fShaderAttributeDef)) { + throw std::runtime_error("Invalid shader attribute definition"); + } +} + +UsdShaderAttributeDef::~UsdShaderAttributeDef() { } + +std::string UsdShaderAttributeDef::name() const +{ + TF_AXIOM(fShaderAttributeDef); + return fShaderAttributeDef->GetName().GetString(); +} + +std::string UsdShaderAttributeDef::type() const +{ + TF_AXIOM(fShaderAttributeDef); + const PXR_NS::SdfValueTypeName typeName = fShaderAttributeDef->GetTypeAsSdfType().first; + return usdTypeToUfe(typeName); +} + +std::string UsdShaderAttributeDef::defaultValue() const +{ + TF_AXIOM(fShaderAttributeDef); + std::ostringstream defaultValue; + defaultValue << fShaderAttributeDef->GetDefaultValue(); + return defaultValue.str(); +} + +Ufe::AttributeDef::IOType UsdShaderAttributeDef::ioType() const +{ + TF_AXIOM(fShaderAttributeDef); + return fShaderAttributeDef->IsOutput() ? Ufe::AttributeDef::OUTPUT_ATTR + : Ufe::AttributeDef::INPUT_ATTR; +} + +Ufe::Value UsdShaderAttributeDef::getMetadata(const std::string& key) const +{ + TF_AXIOM(fShaderAttributeDef); + const PXR_NS::NdrTokenMap& metadata = fShaderAttributeDef->GetMetadata(); + auto it = metadata.find(PXR_NS::TfToken(key)); + if (it != metadata.cend()) { + return Ufe::Value(it->second); + } + // TODO: Adapt UI metadata information found in SdrShaderProperty to Ufe standards + // TODO: Fix Mtlx parser in USD to populate UI metadata in SdrShaderProperty + return {}; +} + +bool UsdShaderAttributeDef::hasMetadata(const std::string& key) const +{ + TF_AXIOM(fShaderAttributeDef); + const PXR_NS::NdrTokenMap& metadata = fShaderAttributeDef->GetMetadata(); + auto it = metadata.find(PXR_NS::TfToken(key)); + return (it != metadata.cend()); +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdShaderAttributeDef.h b/lib/mayaUsd/ufe/UsdShaderAttributeDef.h new file mode 100644 index 0000000000..3a01246be4 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdShaderAttributeDef.h @@ -0,0 +1,70 @@ +// +// Copyright 2022 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 + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief UsdShaderAttributeDef interface. +class MAYAUSD_CORE_PUBLIC UsdShaderAttributeDef : public Ufe::AttributeDef +{ +public: + typedef std::shared_ptr Ptr; + + UsdShaderAttributeDef(const PXR_NS::SdrShaderPropertyConstPtr& shaderAttributeDef); + ~UsdShaderAttributeDef(); + + // Delete the copy/move constructors assignment operators. + UsdShaderAttributeDef(const UsdShaderAttributeDef&) = delete; + UsdShaderAttributeDef& operator=(const UsdShaderAttributeDef&) = delete; + UsdShaderAttributeDef(UsdShaderAttributeDef&&) = delete; + UsdShaderAttributeDef& operator=(UsdShaderAttributeDef&&) = delete; + + //! \return The attributeDef's name. + std::string name() const override; + + //! \return The attributeDef's type. + std::string type() const override; + + //! \return The string representation of the attributeDef's value. + std::string defaultValue() const override; + + //! \return The attribute input/output type. + IOType ioType() const override; + + /*! + Get the value of the metadata named key. + \param[in] key The metadata key to query. + \return The value of the metadata key. If the key does not exist an empty Value is returned. + */ + Ufe::Value getMetadata(const std::string& key) const override; + + //! Returns true if metadata key has a non-empty value. + bool hasMetadata(const std::string& key) const override; + +private: + const PXR_NS::SdrShaderPropertyConstPtr fShaderAttributeDef; + +}; // UsdShaderAttributeDef + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdShaderNodeDef.cpp b/lib/mayaUsd/ufe/UsdShaderNodeDef.cpp index daec2863e3..001dee2a0d 100644 --- a/lib/mayaUsd/ufe/UsdShaderNodeDef.cpp +++ b/lib/mayaUsd/ufe/UsdShaderNodeDef.cpp @@ -16,9 +16,16 @@ #include "UsdShaderNodeDef.h" +#if (UFE_PREVIEW_VERSION_NUM >= 4010) +#include +#include +#endif + #include "Global.h" #include "Utils.h" +#include +#include #include #include #include @@ -31,22 +38,27 @@ namespace MAYAUSD_NS_DEF { namespace ufe { +PXR_NAMESPACE_USING_DIRECTIVE + constexpr char UsdShaderNodeDef::kNodeDefCategoryShader[]; -Ufe::Attribute::Type getUfeTypeForAttribute(const PXR_NS::SdrShaderPropertyConstPtr& shaderProperty) +#if (UFE_PREVIEW_VERSION_NUM < 4010) +Ufe::Attribute::Type getUfeTypeForAttribute(const SdrShaderPropertyConstPtr& shaderProperty) { - const PXR_NS::SdfValueTypeName typeName = shaderProperty->GetTypeAsSdfType().first; + const SdfValueTypeName typeName = shaderProperty->GetTypeAsSdfType().first; return usdTypeToUfe(typeName); } +#endif template -Ufe::ConstAttributeDefs getAttrs(const PXR_NS::SdrShaderNodeConstPtr& shaderNodeDef) +Ufe::ConstAttributeDefs getAttrs(const SdrShaderNodeConstPtr& shaderNodeDef) { Ufe::ConstAttributeDefs attrs; const bool input = (IOTYPE == Ufe::AttributeDef::INPUT_ATTR); auto names = input ? shaderNodeDef->GetInputNames() : shaderNodeDef->GetOutputNames(); - for (const PXR_NS::TfToken& name : names) { - PXR_NS::SdrShaderPropertyConstPtr property + attrs.reserve(names.size()); + for (const TfToken& name : names) { + SdrShaderPropertyConstPtr property = input ? shaderNodeDef->GetShaderInput(name) : shaderNodeDef->GetShaderOutput(name); if (!property) { // Cannot do much if the pointer is null. This can happen if the type_info for a @@ -54,26 +66,33 @@ Ufe::ConstAttributeDefs getAttrs(const PXR_NS::SdrShaderNodeConstPtr& shaderNode // SdrNode::GetShaderInput has to downcast a NdrProperty pointer. continue; } +#if (UFE_PREVIEW_VERSION_NUM < 4010) std::ostringstream defaultValue; defaultValue << property->GetDefaultValue(); Ufe::Attribute::Type type = getUfeTypeForAttribute(property); - attrs.push_back(Ufe::AttributeDef::create(name, type, defaultValue.str(), IOTYPE)); + attrs.emplace_back(Ufe::AttributeDef::create(name, type, defaultValue.str(), IOTYPE)); +#else + attrs.emplace_back(Ufe::AttributeDef::ConstPtr(new UsdShaderAttributeDef(property))); +#endif } return attrs; } -UsdShaderNodeDef::UsdShaderNodeDef(const PXR_NS::SdrShaderNodeConstPtr& shaderNodeDef) +UsdShaderNodeDef::UsdShaderNodeDef(const SdrShaderNodeConstPtr& shaderNodeDef) : Ufe::NodeDef() +#if (UFE_PREVIEW_VERSION_NUM < 4010) , fType(shaderNodeDef ? shaderNodeDef->GetName() : "") +#endif , fShaderNodeDef(shaderNodeDef) +#if (UFE_PREVIEW_VERSION_NUM < 4010) , fInputs( shaderNodeDef ? getAttrs(shaderNodeDef) : Ufe::ConstAttributeDefs()) , fOutputs( shaderNodeDef ? getAttrs(shaderNodeDef) : Ufe::ConstAttributeDefs()) +#endif { - PXR_NAMESPACE_USING_DIRECTIVE if (!TF_VERIFY(fShaderNodeDef)) { throw std::runtime_error("Invalid shader node definition"); } @@ -81,13 +100,190 @@ UsdShaderNodeDef::UsdShaderNodeDef(const PXR_NS::SdrShaderNodeConstPtr& shaderNo UsdShaderNodeDef::~UsdShaderNodeDef() { } +#if (UFE_PREVIEW_VERSION_NUM < 4010) const std::string& UsdShaderNodeDef::type() const { return fType; } const Ufe::ConstAttributeDefs& UsdShaderNodeDef::inputs() const { return fInputs; } const Ufe::ConstAttributeDefs& UsdShaderNodeDef::outputs() const { return fOutputs; } -UsdShaderNodeDef::Ptr UsdShaderNodeDef::create(const PXR_NS::SdrShaderNodeConstPtr& shaderNodeDef) +#else + +std::string UsdShaderNodeDef::type() const +{ + TF_AXIOM(fShaderNodeDef); + return fShaderNodeDef->GetName(); +} + +std::size_t UsdShaderNodeDef::nbClassifications() const +{ + TF_AXIOM(fShaderNodeDef); + + // Based on a review of all items found in the Sdr registry as of USD 21.11: + + // UsdLux shaders provide 2 classification levels: + // - Context + // - SourceType + if (fShaderNodeDef->GetFamily().IsEmpty()) { + return 2; + } + + // Regular shader nodes provide 3 classification levels: + // - family + // - role + // - sourceType + return 3; +} + +std::string UsdShaderNodeDef::classification(std::size_t level) const +{ + TF_AXIOM(fShaderNodeDef); + if (fShaderNodeDef->GetFamily().IsEmpty()) { + switch (level) { + // UsdLux: + case 0: return fShaderNodeDef->GetContext().GetString(); + case 1: return fShaderNodeDef->GetSourceType().GetString(); + } + } + switch (level) { + // UsdShade: These work with MaterialX and Preview surface. Need to recheck against + // third-party renderers as we discover their shading nodes. + case 0: { + return fShaderNodeDef->GetFamily().GetString(); + } + case 1: { + if (fShaderNodeDef->GetRole() == fShaderNodeDef->GetName()) { + // See https://github.com/AcademySoftwareFoundation/MaterialX/issues/921 + return "other"; + } else { + return fShaderNodeDef->GetRole(); + } + } + case 2: { + return fShaderNodeDef->GetSourceType().GetString(); + } + } + return {}; +} + +std::vector UsdShaderNodeDef::inputNames() const +{ + TF_AXIOM(fShaderNodeDef); + std::vector retVal; + auto names = fShaderNodeDef->GetInputNames(); + retVal.reserve(names.size()); + for (auto&& n : names) { + retVal.emplace_back(n.GetString()); + } + return retVal; +} + +bool UsdShaderNodeDef::hasInput(const std::string& name) const +{ + TF_AXIOM(fShaderNodeDef); + return fShaderNodeDef->GetShaderInput(TfToken(name)); +} + +Ufe::AttributeDef::ConstPtr UsdShaderNodeDef::input(const std::string& name) const +{ + TF_AXIOM(fShaderNodeDef); + if (SdrShaderPropertyConstPtr property = fShaderNodeDef->GetShaderInput(TfToken(name))) { + return Ufe::AttributeDef::ConstPtr(new UsdShaderAttributeDef(property)); + } + return {}; +} + +Ufe::ConstAttributeDefs UsdShaderNodeDef::inputs() const +{ + TF_AXIOM(fShaderNodeDef); + return getAttrs(fShaderNodeDef); +} + +std::vector UsdShaderNodeDef::outputNames() const +{ + TF_AXIOM(fShaderNodeDef); + std::vector retVal; + auto names = fShaderNodeDef->GetOutputNames(); + retVal.reserve(names.size()); + for (auto&& n : names) { + retVal.emplace_back(n.GetString()); + } + return retVal; +} + +bool UsdShaderNodeDef::hasOutput(const std::string& name) const +{ + TF_AXIOM(fShaderNodeDef); + return fShaderNodeDef->GetShaderOutput(TfToken(name)); +} + +Ufe::AttributeDef::ConstPtr UsdShaderNodeDef::output(const std::string& name) const +{ + TF_AXIOM(fShaderNodeDef); + if (SdrShaderPropertyConstPtr property = fShaderNodeDef->GetShaderOutput(TfToken(name))) { + return Ufe::AttributeDef::ConstPtr(new UsdShaderAttributeDef(property)); + } + return {}; +} + +Ufe::ConstAttributeDefs UsdShaderNodeDef::outputs() const +{ + TF_AXIOM(fShaderNodeDef); + return getAttrs(fShaderNodeDef); +} + +Ufe::Value UsdShaderNodeDef::getMetadata(const std::string& key) const +{ + TF_AXIOM(fShaderNodeDef); + const NdrTokenMap& metadata = fShaderNodeDef->GetMetadata(); + auto it = metadata.find(TfToken(key)); + if (it != metadata.cend()) { + return Ufe::Value(it->second); + } + // TODO: Adapt UI metadata information found in SdrShaderNode to Ufe standards + // TODO: Fix Mtlx parser in USD to populate UI metadata in SdrShaderNode + return {}; +} + +bool UsdShaderNodeDef::hasMetadata(const std::string& key) const +{ + TF_AXIOM(fShaderNodeDef); + const NdrTokenMap& metadata = fShaderNodeDef->GetMetadata(); + auto it = metadata.find(TfToken(key)); + return it != metadata.cend(); +} + +Ufe::SceneItem::Ptr UsdShaderNodeDef::createNode( + const Ufe::SceneItem::Ptr& parent, + const Ufe::PathComponent& name) const +{ + TF_AXIOM(fShaderNodeDef); + UsdSceneItem::Ptr parentItem = std::dynamic_pointer_cast(parent); + if (parentItem) { + UsdUndoCreateFromNodeDefCommand::Ptr cmd + = UsdUndoCreateFromNodeDefCommand::create(fShaderNodeDef, parentItem, name.string()); + if (cmd) { + cmd->execute(); + return cmd->insertedChild(); + } + } + return {}; +} + +Ufe::InsertChildCommand::Ptr UsdShaderNodeDef::createNodeCmd( + const Ufe::SceneItem::Ptr& parent, + const Ufe::PathComponent& name) const +{ + TF_AXIOM(fShaderNodeDef); + UsdSceneItem::Ptr parentItem = std::dynamic_pointer_cast(parent); + if (parentItem) { + return UsdUndoCreateFromNodeDefCommand::create(fShaderNodeDef, parentItem, name.string()); + } + return {}; +} +#endif + +UsdShaderNodeDef::Ptr UsdShaderNodeDef::create(const SdrShaderNodeConstPtr& shaderNodeDef) { try { return std::make_shared(shaderNodeDef); @@ -101,10 +297,11 @@ Ufe::NodeDefs UsdShaderNodeDef::definitions(const std::string& category) Ufe::NodeDefs result; if (category == std::string(Ufe::NodeDefHandler::kNodeDefCategoryAll) || category == std::string(kNodeDefCategoryShader)) { - PXR_NS::SdrRegistry& registry = PXR_NS::SdrRegistry::GetInstance(); - PXR_NS::SdrShaderNodePtrVec shaderNodeDefs = registry.GetShaderNodesByFamily(); - for (const PXR_NS::SdrShaderNodeConstPtr& shaderNodeDef : shaderNodeDefs) { - result.push_back(UsdShaderNodeDef::create(shaderNodeDef)); + SdrRegistry& registry = SdrRegistry::GetInstance(); + SdrShaderNodePtrVec shaderNodeDefs = registry.GetShaderNodesByFamily(); + result.reserve(shaderNodeDefs.size()); + for (const SdrShaderNodeConstPtr& shaderNodeDef : shaderNodeDefs) { + result.emplace_back(UsdShaderNodeDef::create(shaderNodeDef)); } } return result; diff --git a/lib/mayaUsd/ufe/UsdShaderNodeDef.h b/lib/mayaUsd/ufe/UsdShaderNodeDef.h index d41094c13c..ac387536b6 100644 --- a/lib/mayaUsd/ufe/UsdShaderNodeDef.h +++ b/lib/mayaUsd/ufe/UsdShaderNodeDef.h @@ -41,6 +41,7 @@ class MAYAUSD_CORE_PUBLIC UsdShaderNodeDef : public Ufe::NodeDef UsdShaderNodeDef(UsdShaderNodeDef&&) = delete; UsdShaderNodeDef& operator=(UsdShaderNodeDef&&) = delete; +#if (UFE_PREVIEW_VERSION_NUM < 4010) //! \return The type of the shader node definition. const std::string& type() const override; @@ -49,6 +50,95 @@ class MAYAUSD_CORE_PUBLIC UsdShaderNodeDef : public Ufe::NodeDef //! \return The outputs of the shader node definition. const Ufe::ConstAttributeDefs& outputs() const override; +#else + //! \return The type of the shader node definition. + std::string type() const override; + + /*! + Queries the number of classification levels available for this node. + This can vary across runtimes. A biology implementation would have + species as the "type" and genus, family, order, class, phylum, kingdom + representing the 6 available levels. + \return The number of classification levels. + */ + std::size_t nbClassifications() const override; + + /*! + Gets the classification label applicable to this NodeNef for the + requested classification level. The most precise classification level + corresponds to level zero. + \param level The classification level to query. + \return The classification label for this node at this level. + */ + std::string classification(std::size_t level) const override; + + //! \return List of all the input names for this node definition. + std::vector inputNames() const override; + + /*! + Queries whether an input exists with the given name. + \param name The input name to check. + \return True if the object contains an input matching the name. + */ + bool hasInput(const std::string& name) const override; + + /*! + Creates an AttributeDef interface for the given input name. + \param name Name of the input to retrieve. + \return AttributeDef interface for the given name. Returns a null + pointer if no input exists for the given name. + */ + Ufe::AttributeDef::ConstPtr input(const std::string& name) const override; + + //! \return The inputs of the shader node definition. + Ufe::ConstAttributeDefs inputs() const override; + + //! \return List of all the output names for this node definition. + std::vector outputNames() const override; + + /*! + Queries whether an output exists with the given name. + \param name The output name to check. + \return True if the object contains an output matching the name. + */ + bool hasOutput(const std::string& name) const override; + + /*! + Creates an AttributeDef interface for the given output name. + \param name Name of the output to retrieve. + \return AttributeDef interface for the given name. Returns a null + pointer if no output exists for the given name. + */ + Ufe::AttributeDef::ConstPtr output(const std::string& name) const override; + + //! \return The outputs of the shader node definition. + Ufe::ConstAttributeDefs outputs() const override; + + /*! + Get the value of the metadata named key. + \param[in] key The metadata key to query. + \return The value of the metadata key. If the key does not exist an empty Value is returned. + */ + Ufe::Value getMetadata(const std::string& key) const override; + + //! Returns true if metadata key has a non-empty value. + bool hasMetadata(const std::string& key) const override; + + //! Create a SceneItem using the current node definition as template. + //! \param parent Item under which the node is to be created. + //! \param name Name of the new node. + //! \return SceneItem for the created node, at its new path. + Ufe::SceneItem::Ptr + createNode(const Ufe::SceneItem::Ptr& parent, const Ufe::PathComponent& name) const override; + + //! Create a command to create a SceneItem using the current node definition + //! as template. The command is not executed. + //! \param parent Item under which the node is to be created. + //! \param name Name of the new node. + //! \return Command whose execution will create the node. + Ufe::InsertChildCommand::Ptr + createNodeCmd(const Ufe::SceneItem::Ptr& parent, const Ufe::PathComponent& name) const override; +#endif //! Create a UsdShaderNodeDef. static Ptr create(const PXR_NS::SdrShaderNodeConstPtr& shaderNodeDef); @@ -57,10 +147,14 @@ class MAYAUSD_CORE_PUBLIC UsdShaderNodeDef : public Ufe::NodeDef static Ufe::NodeDefs definitions(const std::string& category); private: - const std::string fType; +#if (UFE_PREVIEW_VERSION_NUM < 4010) + const std::string fType; +#endif const PXR_NS::SdrShaderNodeConstPtr fShaderNodeDef; - const Ufe::ConstAttributeDefs fInputs; - const Ufe::ConstAttributeDefs fOutputs; +#if (UFE_PREVIEW_VERSION_NUM < 4010) + const Ufe::ConstAttributeDefs fInputs; + const Ufe::ConstAttributeDefs fOutputs; +#endif }; // UsdShaderNodeDef diff --git a/lib/mayaUsd/ufe/UsdShaderNodeDefHandler.cpp b/lib/mayaUsd/ufe/UsdShaderNodeDefHandler.cpp index 7d6923f436..070fba2aeb 100644 --- a/lib/mayaUsd/ufe/UsdShaderNodeDefHandler.cpp +++ b/lib/mayaUsd/ufe/UsdShaderNodeDefHandler.cpp @@ -51,16 +51,35 @@ Ufe::NodeDef::Ptr UsdShaderNodeDefHandler::definition(const Ufe::SceneItem::Ptr& } PXR_NS::UsdPrim prim = usdItem->prim(); PXR_NS::UsdShadeShader shader(prim); - PXR_NS::TfToken mxNodeType; + if (!shader) { + return nullptr; + } + PXR_NS::TfToken mxNodeType; shader.GetIdAttr().Get(&mxNodeType); - std::string type = mxNodeType.GetString(); - return definition(type); + + // Careful around name and identifier. They are not the same concept. + // + // Here is one example from MaterialX to illustrate: + // + // ND_standard_surface_surfaceshader exists in 2 versions with identifiers: + // ND_standard_surface_surfaceshader (latest version) + // ND_standard_surface_surfaceshader_100 (version 1.0.0) + // Same name, 2 different identifiers. + PXR_NS::SdrRegistry& registry = PXR_NS::SdrRegistry::GetInstance(); + PXR_NS::SdrShaderNodeConstPtr shaderNodeDef = registry.GetShaderNodeByIdentifier(mxNodeType); + if (!shaderNodeDef) { + return nullptr; + } + return UsdShaderNodeDef::create(shaderNodeDef); } Ufe::NodeDef::Ptr UsdShaderNodeDefHandler::definition(const std::string& type) const { PXR_NS::SdrRegistry& registry = PXR_NS::SdrRegistry::GetInstance(); PXR_NS::SdrShaderNodeConstPtr shaderNodeDef = registry.GetShaderNodeByName(type); + if (!shaderNodeDef) { + return nullptr; + } return UsdShaderNodeDef::create(shaderNodeDef); } diff --git a/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.cpp b/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.cpp new file mode 100644 index 0000000000..d8aad50d90 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.cpp @@ -0,0 +1,79 @@ +// +// Copyright 2022 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 "UsdUndoCreateFromNodeDefCommand.h" + +#include + +#include +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +UsdUndoCreateFromNodeDefCommand::UsdUndoCreateFromNodeDefCommand( + const PXR_NS::SdrShaderNodeConstPtr shaderNodeDef, + const UsdSceneItem::Ptr& parentItem, + const Ufe::PathComponent& name) + : Ufe::InsertChildCommand() + , _shaderNodeDef(shaderNodeDef) + , _addPrimCmd(UsdUndoAddNewPrimCommand::create(parentItem, name.string(), "Shader")) +{ +} + +UsdUndoCreateFromNodeDefCommand::~UsdUndoCreateFromNodeDefCommand() { } + +UsdUndoCreateFromNodeDefCommand::Ptr UsdUndoCreateFromNodeDefCommand::create( + const PXR_NS::SdrShaderNodeConstPtr shaderNodeDef, + const UsdSceneItem::Ptr& parentItem, + const Ufe::PathComponent& name) +{ + return std::make_shared(shaderNodeDef, parentItem, name); +} + +Ufe::SceneItem::Ptr UsdUndoCreateFromNodeDefCommand::insertedChild() const +{ + return UsdSceneItem::create(_addPrimCmd->newUfePath(), _addPrimCmd->newPrim()); +} + +void UsdUndoCreateFromNodeDefCommand::execute() +{ + _addPrimCmd->execute(); + setIdAttr(); +} + +void UsdUndoCreateFromNodeDefCommand::undo() +{ + _addPrimCmd->undo(); + // Nothing to do for the node:id attribute. It will get deleted with the prim. +} + +void UsdUndoCreateFromNodeDefCommand::redo() +{ + _addPrimCmd->redo(); + setIdAttr(); +} + +void UsdUndoCreateFromNodeDefCommand::setIdAttr() +{ + UsdShadeShader shader(_addPrimCmd->newPrim()); + shader.CreateIdAttr(VtValue(_shaderNodeDef->GetIdentifier())); +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.h b/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.h new file mode 100644 index 0000000000..9dbb53aa86 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdUndoCreateFromNodeDefCommand.h @@ -0,0 +1,71 @@ +// +// Copyright 2022 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 UsdUndoCreateFromNodeDefCommand +class MAYAUSD_CORE_PUBLIC UsdUndoCreateFromNodeDefCommand : public Ufe::InsertChildCommand +{ +public: + typedef std::shared_ptr Ptr; + + UsdUndoCreateFromNodeDefCommand( + const PXR_NS::SdrShaderNodeConstPtr shaderNodeDef, + const UsdSceneItem::Ptr& parentItem, + const Ufe::PathComponent& name); + ~UsdUndoCreateFromNodeDefCommand() override; + + // Delete the copy/move constructors assignment operators. + UsdUndoCreateFromNodeDefCommand(const UsdUndoCreateFromNodeDefCommand&) = delete; + UsdUndoCreateFromNodeDefCommand& operator=(const UsdUndoCreateFromNodeDefCommand&) = delete; + UsdUndoCreateFromNodeDefCommand(UsdUndoCreateFromNodeDefCommand&&) = delete; + UsdUndoCreateFromNodeDefCommand& operator=(UsdUndoCreateFromNodeDefCommand&&) = delete; + + //! Create a UsdUndoCreateFromNodeDefCommand from a USD scene item and a UFE path component. + static UsdUndoCreateFromNodeDefCommand::Ptr create( + const PXR_NS::SdrShaderNodeConstPtr shaderNodeDef, + const UsdSceneItem::Ptr& parentItem, + const Ufe::PathComponent& name); + + Ufe::SceneItem::Ptr insertedChild() const override; + + void execute() override; + void undo() override; + void redo() override; + +private: + const PXR_NS::SdrShaderNodeConstPtr _shaderNodeDef; + + std::shared_ptr _addPrimCmd; + + void setIdAttr(); +}; // UsdUndoCreateFromNodeDefCommand + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py index 04dcbf9483..c7bd81ab1c 100644 --- a/test/lib/ufe/testContextOps.py +++ b/test/lib/ufe/testContextOps.py @@ -341,8 +341,7 @@ def testAddNewPrim(self): @unittest.skipUnless(Usd.GetVersion() >= (0, 21, 8), 'Requires CanApplySchema from USD') def testMaterialBinding(self): - """In this test we will go as far as possible towards creating and binding a working - material using only Ufe and Maya commands (for full undo capabilities)""" + """This test builds a material using only Ufe pre-4.10 capabilities.""" cmds.file(new=True, force=True) # Create a proxy shape with empty stage to start with. @@ -366,9 +365,7 @@ def testMaterialBinding(self): materialItem = rootHier.children()[-1] contextOps = ufe.ContextOps.contextOps(materialItem) - - # TODO: We want to create that shader directly from a Ufe.ShaderNodeDef. This will take care - # of the "info:id" automatically and potentially provide the authorable attributes. + cmd = contextOps.doOpCmd(['Add New Prim', 'Shader']) ufeCmd.execute(cmd) @@ -383,9 +380,6 @@ def testMaterialBinding(self): shaderAttr = shaderAttrs.attribute("info:id") shaderAttr.set("ND_standard_surface_surfaceshader") - # TODO: Set base_color to red - # TODO: Connect "/Material1.outputs:mtlx:surface" to "/Material1/Shader1.outputs:surface" - # Now that we have a material, we can bind it on the capsule item even if incomplete capsuleItem = rootHier.children()[0] capsulePrim = usdUtils.getPrimFromSceneItem(capsuleItem) @@ -415,6 +409,87 @@ 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 testMaterialBindingWithNodeDefHandler(self): + """In this test we will go as far as possible towards creating and binding a working + material using only Ufe and Maya commands (for full undo capabilities). It is locked + at what Ufe 0.4.10 offers.""" + 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) + cmd = contextOps.doOpCmd(['Add New Prim', 'Material']) + ufeCmd.execute(cmd) + + rootHier = ufe.Hierarchy.hierarchy(proxyShapeItem) + self.assertTrue(rootHier.hasChildren()) + self.assertEqual(len(rootHier.children()), 2) + + materialItem = rootHier.children()[-1] + contextOps = ufe.ContextOps.contextOps(materialItem) + + shaderName = "ND_standard_surface_surfaceshader" + surfDef = ufe.NodeDef.definition(materialItem.runTimeId(), shaderName) + cmd = surfDef.createNodeCmd(materialItem, ufe.PathComponent("Red1")) + ufeCmd.execute(cmd) + shaderItem = cmd.insertedChild + shaderAttrs = ufe.Attributes.attributes(shaderItem) + + self.assertTrue(shaderAttrs.hasAttribute("info:id")) + self.assertEqual(shaderAttrs.attribute("info:id").get(), shaderName) + self.assertEqual(ufe.PathString.string(shaderItem.path()), "|stage1|stageShape1,/Material1/Red11") + materialHier = ufe.Hierarchy.hierarchy(materialItem) + self.assertTrue(materialHier.hasChildren()) + + cmds.undo() + materialHier = ufe.Hierarchy.hierarchy(materialItem) + self.assertFalse(materialHier.hasChildren()) + + cmds.redo() + materialHier = ufe.Hierarchy.hierarchy(materialItem) + self.assertTrue(materialHier.hasChildren()) + shaderItem = cmd.insertedChild + shaderAttrs = ufe.Attributes.attributes(shaderItem) + + # Now that we have a material, we can bind it on the capsule item even if incomplete + capsuleItem = rootHier.children()[0] + capsulePrim = usdUtils.getPrimFromSceneItem(capsuleItem) + self.assertFalse(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + + contextOps = ufe.ContextOps.contextOps(capsuleItem) + cmd = contextOps.doOpCmd(['Bind Material', '/Material1']) + self.assertTrue(cmd) + ufeCmd.execute(cmd) + self.assertTrue(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + capsuleBindAPI = UsdShade.MaterialBindingAPI(capsulePrim) + self.assertEqual(capsuleBindAPI.GetDirectBinding().GetMaterialPath(), Sdf.Path("/Material1")) + + cmds.undo() + self.assertFalse(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + cmds.redo() + self.assertTrue(capsulePrim.HasAPI(UsdShade.MaterialBindingAPI)) + self.assertEqual(capsuleBindAPI.GetDirectBinding().GetMaterialPath(), Sdf.Path("/Material1")) + + cmd = contextOps.doOpCmd(['Unbind Material']) + self.assertTrue(cmd) + ufeCmd.execute(cmd) + + self.assertTrue(capsuleBindAPI.GetDirectBinding().GetMaterialPath().isEmpty) + cmds.undo() + self.assertEqual(capsuleBindAPI.GetDirectBinding().GetMaterialPath(), Sdf.Path("/Material1")) + cmds.redo() + self.assertTrue(capsuleBindAPI.GetDirectBinding().GetMaterialPath().isEmpty) def testAddNewPrimWithDelete(self): cmds.file(new=True, force=True) diff --git a/test/lib/ufe/testShaderNodeDef.py b/test/lib/ufe/testShaderNodeDef.py index bd10dcf348..e44dbdd71c 100644 --- a/test/lib/ufe/testShaderNodeDef.py +++ b/test/lib/ufe/testShaderNodeDef.py @@ -136,3 +136,37 @@ def testDefinitionByType(self): self.assertEqual(len(outputs), 1) self.assertEqual(outputs[0].name(), "out") self.assertEqual(outputs[0].type(), "ColorFloat3") + + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4010', 'Improvements to nodeDef only available in UFE preview version 0.4.8 and greater') + def testClassificationsAndMetadata(self): + type = "ND_image_color3" + nodeDefHandler = self.getNodeDefHandler() + nodeDef = nodeDefHandler.definition(type) + self.assertIsNotNone(nodeDef) + + # Full input API: + inputNames = nodeDef.inputNames() + self.assertEqual(len(inputNames), 10) + self.assertIn("file", inputNames) + self.assertTrue(nodeDef.hasInput("file")) + self.assertFalse(nodeDef.hasInput("DefinitelyNotAnInput")) + + # Metadata API: + self.assertTrue(nodeDef.hasMetadata("role")) + self.assertEqual(nodeDef.getMetadata("role"), ufe.Value("texture")) + self.assertFalse(nodeDef.hasMetadata("DefinitelyNotAKnownMetadata")) + + # Classifications API: + self.assertGreater(nodeDef.nbClassifications(), 0) + self.assertEqual(nodeDef.classification(0), "image") + + # AttributeDef Metadata API: + fileInput = nodeDef.input("file") + self.assertTrue(fileInput.hasMetadata("__SDR__isAssetIdentifier")) + self.assertFalse(fileInput.hasMetadata("DefinitelyNotAKnownMetadata")) + nodeDef = nodeDefHandler.definition("ND_add_float") + output = nodeDef.output("out") + self.assertEqual(output.getMetadata("__SDR__defaultinput"), ufe.Value("in1")) + + +