diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index d85b3ec8d9..91f7a60000 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -251,6 +251,18 @@ ProxyShapeHierarchy::insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::Sc return insertChildCommand->insertedChild(); } +Ufe::InsertChildCommand::Ptr +ProxyShapeHierarchy::appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) +{ + const auto childCmd = appendChildCmd(child); + + if (childCmd && isConnected(downcast(child))) { + throw std::runtime_error("The node you're trying to move has connections."); + } + + return childCmd; +} + #ifdef UFE_V3_FEATURES_AVAILABLE Ufe::SceneItem::Ptr ProxyShapeHierarchy::createGroup(const Ufe::PathComponent& name) const { diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h index 42a9ac522f..ff583180f8 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.h +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.h @@ -78,6 +78,8 @@ class MAYAUSD_CORE_PUBLIC ProxyShapeHierarchy : public Ufe::Hierarchy insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; Ufe::InsertChildCommand::Ptr insertChildCmd(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; + Ufe::InsertChildCommand::Ptr + appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) override; Ufe::UndoableCommand::Ptr reorderCmd(const Ufe::SceneItemList& orderedList) const override; diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp index 0c92d86498..672d1ead39 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp @@ -81,6 +81,13 @@ Ufe::InsertChildCommand::Ptr PulledObjectHierarchy::insertChildCmd( return nullptr; } +Ufe::InsertChildCommand::Ptr +PulledObjectHierarchy::appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + Ufe::SceneItem::Ptr PulledObjectHierarchy::insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) { diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.h b/lib/mayaUsd/ufe/PulledObjectHierarchy.h index eb38260736..56ea32abc4 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.h +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.h @@ -74,6 +74,8 @@ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; Ufe::InsertChildCommand::Ptr insertChildCmd(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; + Ufe::InsertChildCommand::Ptr + appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) override; Ufe::UndoableCommand::Ptr reorderCmd(const Ufe::SceneItemList& orderedList) const override; diff --git a/lib/mayaUsd/ufe/UsdAttributes.cpp b/lib/mayaUsd/ufe/UsdAttributes.cpp index 207d5da94a..cec0e61627 100644 --- a/lib/mayaUsd/ufe/UsdAttributes.cpp +++ b/lib/mayaUsd/ufe/UsdAttributes.cpp @@ -446,7 +446,7 @@ static void removeSrcAttrConnections(PXR_NS::UsdPrim& prim, const PXR_NS::UsdAtt for (const auto& attribute : prim.GetAttributes()) { PXR_NS::UsdAttribute dstUsdAttr = attribute.As(); - if (MayaUsd::ufe::isConnected(srcUsdAttr, dstUsdAttr)) { + if (isConnected(srcUsdAttr, dstUsdAttr)) { UsdShadeConnectableAPI::DisconnectSource(dstUsdAttr, srcUsdAttr); // Check if we can remove the property. if (MayaUsd::ufe::canRemoveDstProperty(dstUsdAttr)) { diff --git a/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp b/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp index 510232ff96..efba172c0f 100644 --- a/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp +++ b/lib/mayaUsd/ufe/UsdUndoConnectionCommands.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -155,7 +156,7 @@ void UsdUndoCreateConnectionCommand::execute() return; } - if (MayaUsd::ufe::isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { + if (isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { return; } @@ -287,7 +288,7 @@ void UsdUndoDeleteConnectionCommand::execute() UsdAttribute* dstUsdAttr = usdAttrFromUfeAttr(dstAttr); if (!srcUsdAttr || !dstUsdAttr - || !MayaUsd::ufe::isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { + || !isConnected(srcUsdAttr->usdAttribute(), dstUsdAttr->usdAttribute())) { return; } #if PXR_VERSION < 2302 diff --git a/lib/mayaUsd/ufe/Utils.cpp b/lib/mayaUsd/ufe/Utils.cpp index d2f0beb86c..59a4dfb7a2 100644 --- a/lib/mayaUsd/ufe/Utils.cpp +++ b/lib/mayaUsd/ufe/Utils.cpp @@ -347,20 +347,6 @@ TfTokenVector getProxyShapePurposes(const Ufe::Path& path) return purposes; } -bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr) -{ - PXR_NS::SdfPathVector connectedAttrs; - dstUsdAttr.GetConnections(&connectedAttrs); - - for (PXR_NS::SdfPath path : connectedAttrs) { - if (path == srcUsdAttr.GetPath()) { - return true; - } - } - - return false; -} - bool canRemoveSrcProperty(const PXR_NS::UsdAttribute& srcAttr) { diff --git a/lib/mayaUsd/ufe/Utils.h b/lib/mayaUsd/ufe/Utils.h index d4aa72c014..e9c80c97ba 100644 --- a/lib/mayaUsd/ufe/Utils.h +++ b/lib/mayaUsd/ufe/Utils.h @@ -116,11 +116,6 @@ PXR_NS::UsdTimeCode getTime(const Ufe::Path& path); MAYAUSD_CORE_PUBLIC PXR_NS::TfTokenVector getProxyShapePurposes(const Ufe::Path& path); -//! Check if the src and dst attributes are connected. -//! \return True, if they are connected. -MAYAUSD_CORE_PUBLIC -bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr); - //! Check if a source connection property is allowed to be removed. //! \return True, if the property can be removed. MAYAUSD_CORE_PUBLIC diff --git a/lib/usdUfe/ufe/UsdHierarchy.cpp b/lib/usdUfe/ufe/UsdHierarchy.cpp index cab2e62a84..294607d521 100644 --- a/lib/usdUfe/ufe/UsdHierarchy.cpp +++ b/lib/usdUfe/ufe/UsdHierarchy.cpp @@ -257,6 +257,19 @@ UsdHierarchy::insertChildCmd(const Ufe::SceneItem::Ptr& child, const Ufe::SceneI return UsdUndoInsertChildCommand::create(fItem, downcast(child), downcast(pos)); } +Ufe::InsertChildCommand::Ptr +UsdHierarchy::appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) +{ + const auto childCmd = appendChildCmd(child); + + if (childCmd && isConnected(downcast(child))) { + throw std::runtime_error("The node you're trying to move has connections."); + } + + return childCmd; +} + + Ufe::SceneItem::Ptr UsdHierarchy::insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) { diff --git a/lib/usdUfe/ufe/UsdHierarchy.h b/lib/usdUfe/ufe/UsdHierarchy.h index 7d37377f59..aa4128ef97 100644 --- a/lib/usdUfe/ufe/UsdHierarchy.h +++ b/lib/usdUfe/ufe/UsdHierarchy.h @@ -84,6 +84,8 @@ class USDUFE_PUBLIC UsdHierarchy : public Ufe::Hierarchy insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; Ufe::InsertChildCommand::Ptr insertChildCmd(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; + Ufe::InsertChildCommand::Ptr + appendChildVerifyRestrictionsCmd(const Ufe::SceneItem::Ptr& child) override; Ufe::UndoableCommand::Ptr reorderCmd(const Ufe::SceneItemList& orderedList) const override; diff --git a/lib/usdUfe/ufe/Utils.cpp b/lib/usdUfe/ufe/Utils.cpp index 42c04d5456..9b99174b9d 100644 --- a/lib/usdUfe/ufe/Utils.cpp +++ b/lib/usdUfe/ufe/Utils.cpp @@ -428,6 +428,95 @@ Ufe::BBox3d combineUfeBBox(const Ufe::BBox3d& ufeBBox1, const Ufe::BBox3d& ufeBB return combinedBBox; } +bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr) +{ + PXR_NS::SdfPathVector connectedAttrs; + dstUsdAttr.GetConnections(&connectedAttrs); + + for (PXR_NS::SdfPath path : connectedAttrs) { + if (path == srcUsdAttr.GetPath()) { + return true; + } + } + + return false; +} + +bool isConnected(const UsdSceneItem::Ptr& usdItem) +{ + + if (!usdItem) { + return false; + } + + const auto prim = usdItem->prim(); + + if (!prim) { + return false; + } + + const auto primAttrs = prim.GetAuthoredAttributes(); + + for (const auto& attr : primAttrs) { + const auto kBaseNameAndType + = PXR_NS::UsdShadeUtils::GetBaseNameAndType(PXR_NS::TfToken(attr.GetName())); + + if (kBaseNameAndType.second != PXR_NS::UsdShadeAttributeType::Output + && kBaseNameAndType.second != PXR_NS::UsdShadeAttributeType::Input) { + continue; + } + + if (kBaseNameAndType.second == PXR_NS::UsdShadeAttributeType::Input) { + // The attribute could be a destination for connected sources, so check for its + // connections. + PXR_NS::UsdShadeSourceInfoVector sourcesInfo + = pxr::UsdShadeConnectableAPI::GetConnectedSources(attr); + + if (!sourcesInfo.empty()) { + return true; + } + } + + if (kBaseNameAndType.second == PXR_NS::UsdShadeAttributeType::Output) { + const auto primParent = prim.GetParent(); + + if (!primParent) { + continue; + } + + // The attribute could be a source connection, we have to explore the siblings. + for (auto&& child : primParent.GetChildren()) { + if (child == prim) { + continue; + } + + for (const auto& otherAttr : child.GetAttributes()) { + const auto childAttrBaseNameAndType = PXR_NS::UsdShadeUtils::GetBaseNameAndType( + PXR_NS::TfToken(otherAttr.GetName())); + + if (childAttrBaseNameAndType.second == PXR_NS::UsdShadeAttributeType::Input + && isConnected(attr, otherAttr)) { + return true; + } + } + } + + // Check also if there are connections to the parent. + for (const auto& otherAttr : primParent.GetAttributes()) { + const auto parentAttrBaseNameAndType = PXR_NS::UsdShadeUtils::GetBaseNameAndType( + PXR_NS::TfToken(otherAttr.GetName())); + + if (parentAttrBaseNameAndType.second == PXR_NS::UsdShadeAttributeType::Output + && isConnected(attr, otherAttr)) { + return true; + } + } + } + } + + return false; +} + void applyRootLayerMetadataRestriction(const UsdPrim& prim, const std::string& commandName) { // return early if prim is the pseudo-root. diff --git a/lib/usdUfe/ufe/Utils.h b/lib/usdUfe/ufe/Utils.h index 7ff9b1cb89..febc00b78f 100644 --- a/lib/usdUfe/ufe/Utils.h +++ b/lib/usdUfe/ufe/Utils.h @@ -304,6 +304,16 @@ bool isEditTargetLayerModifiable( USDUFE_PUBLIC Ufe::BBox3d combineUfeBBox(const Ufe::BBox3d& ufeBBox1, const Ufe::BBox3d& ufeBBox2); +//! Check if the src and dst attributes are connected. +//! \return True, if they are connected. +USDUFE_PUBLIC +bool isConnected(const PXR_NS::UsdAttribute& srcUsdAttr, const PXR_NS::UsdAttribute& dstUsdAttr); + +//! Check if the usdItem is connected (i.e. if there are in or out connections). +//! \return True, if it is connected. +USDUFE_PUBLIC +bool isConnected(const UsdSceneItem::Ptr& usdItem); + //! Set both the start and stop wait cursor functions. USDUFE_PUBLIC void setWaitCursorFns(WaitCursorFn startFn, WaitCursorFn stopFn);