From 41392db1def21cbf2f28a16ffaf2ce3a84727c4b Mon Sep 17 00:00:00 2001 From: Anton Khelou Date: Tue, 28 Apr 2026 14:56:28 -0400 Subject: [PATCH 1/2] Add new reference workflow implementation --- lib/mayaUsd/ufe/MayaUsdContextOps.cpp | 56 ++++++++- lib/usdUfe/ufe/CMakeLists.txt | 2 + .../UsdUndoAddReferenceToNewPrimCommand.cpp | 112 ++++++++++++++++++ .../ufe/UsdUndoAddReferenceToNewPrimCommand.h | 67 +++++++++++ 4 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.cpp create mode 100644 lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h diff --git a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp index 6496e6fce..d5bc0e17a 100644 --- a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp +++ b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -103,8 +106,10 @@ static constexpr char kAddNewMaterialLabel[] = "Add New Material"; static constexpr char kAssignExistingMaterialItem[] = "Assign Existing Material"; static constexpr char kAssignExistingMaterialLabel[] = "Assign Existing Material"; #endif -static constexpr char kAddRefOrPayloadLabel[] = "Add..."; +static constexpr char kAddRefOrPayloadLabel[] = "Add to Prim..."; static constexpr char kAddRefOrPayloadItem[] = "AddReferenceOrPayload"; +static constexpr char kAddRefToNewPrimItem[] = "AddReferenceToNewPrim"; +static constexpr char kAddRefToNewPrimLabel[] = "Add..."; const constexpr char kClearAllRefsOrPayloadsLabel[] = "Clear..."; const constexpr char kClearAllRefsOrPayloadsItem[] = "ClearAllReferencesOrPayloads"; const constexpr char kReloadReferenceLabel[] = "Reload"; @@ -644,6 +649,7 @@ Ufe::ContextOps::Items MayaUsdContextOps::getItems(const Ufe::ContextOps::ItemPa assignExistingMaterialItems(_item, itemPath, items); #endif } else if (itemPath[0] == kUSDReferenceItem) { + items.emplace_back(kAddRefToNewPrimItem, kAddRefToNewPrimLabel); items.emplace_back(kAddRefOrPayloadItem, kAddRefOrPayloadLabel); auto prim = _item->prim(); if (prim.HasAuthoredReferences() || prim.HasAuthoredPayloads()) { @@ -757,7 +763,53 @@ Ufe::UndoableCommand::Ptr MayaUsdContextOps::doOpCmd(const ItemPath& itemPath) #endif if (itemPath.size() == 2u && itemPath[0] == kUSDReferenceItem) { - if (itemPath[1] == kAddRefOrPayloadItem) { + if (itemPath[1] == kAddRefToNewPrimItem) { + if (!_prepareUSDReferenceTargetLayer(prim())) + return nullptr; + + MString fileRef = MGlobal::executeCommandStringResult(_selectUSDFileScript()); + if (fileRef.length() == 0) + return nullptr; + + const std::string path = makeUSDReferenceFilePathRelativeIfRequested( + UsdMayaUtil::convert(fileRef), prim()); + if (path.empty()) + return nullptr; + + // Inspect the referenced layer to determine the default prim name and type. + std::string newPrimName; + std::string newPrimType; + SdfLayerRefPtr layer = SdfLayer::FindOrOpen(path); + if (layer && layer->HasDefaultPrim()) { + newPrimName = layer->GetDefaultPrim().GetString(); + SdfPrimSpecHandle primSpec + = layer->GetPrimAtPath(SdfPath("/" + newPrimName)); + if (primSpec) + newPrimType = primSpec->GetTypeName().GetString(); + } + // Fallback: when no default prim, derive the name from the filename stem. + if (newPrimName.empty()) { + std::string stem = path; + const size_t slash = stem.find_last_of("/\\"); + if (slash != std::string::npos) + stem = stem.substr(slash + 1); + const size_t dot = stem.find_last_of('.'); + if (dot != std::string::npos) + stem = stem.substr(0, dot); + newPrimName = TfMakeValidIdentifier(stem); + if (newPrimName.empty()) + newPrimName = "Reference"; + } + + const std::string refPrimPath = UsdMayaUtilFileSystem::getReferencedPrimPath(); + const bool asRef = UsdMayaUtilFileSystem::wantReferenceCompositionArc(); + const bool prepend = UsdMayaUtilFileSystem::wantPrependCompositionArc(); + const bool preload = !asRef && UsdMayaUtilFileSystem::wantPayloadLoaded(); + + return std::make_shared( + prim(), newPrimName, newPrimType, path, refPrimPath, prepend, !asRef, preload); + + } else if (itemPath[1] == kAddRefOrPayloadItem) { if (!_prepareUSDReferenceTargetLayer(prim())) return nullptr; diff --git a/lib/usdUfe/ufe/CMakeLists.txt b/lib/usdUfe/ufe/CMakeLists.txt index c0e61fb49..96f4aac4d 100644 --- a/lib/usdUfe/ufe/CMakeLists.txt +++ b/lib/usdUfe/ufe/CMakeLists.txt @@ -29,6 +29,7 @@ target_sources(${PROJECT_NAME} UsdUndoAddPayloadCommand.cpp UsdUndoAddRefOrPayloadCommand.cpp UsdUndoAddReferenceCommand.cpp + UsdUndoAddReferenceToNewPrimCommand.cpp UsdUndoClearPayloadsCommand.cpp UsdUndoClearReferencesCommand.cpp UsdUndoCreateGroupCommand.cpp @@ -199,6 +200,7 @@ set(HEADERS UsdUndoAddPayloadCommand.h UsdUndoAddRefOrPayloadCommand.h UsdUndoAddReferenceCommand.h + UsdUndoAddReferenceToNewPrimCommand.h UsdUndoClearPayloadsCommand.h UsdUndoClearReferencesCommand.h UsdUndoCreateGroupCommand.h diff --git a/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.cpp b/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.cpp new file mode 100644 index 000000000..d73788a85 --- /dev/null +++ b/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.cpp @@ -0,0 +1,112 @@ +// +// Copyright 2026 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 "UsdUndoAddReferenceToNewPrimCommand.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace USDUFE_NS_DEF { + +PXR_NAMESPACE_USING_DIRECTIVE + +USDUFE_VERIFY_CLASS_SETUP( + UsdUndoableCommand, + UsdUndoAddReferenceToNewPrimCommand); + +/* static */ +UsdListPosition UsdUndoAddReferenceToNewPrimCommand::getListPosition(bool prepend) +{ + return prepend ? UsdListPositionBackOfPrependList : UsdListPositionBackOfAppendList; +} + +UsdUndoAddReferenceToNewPrimCommand::UsdUndoAddReferenceToNewPrimCommand( + const UsdPrim& parentPrim, + const std::string& newPrimName, + const std::string& newPrimType, + const std::string& filePath, + const std::string& primPath, + bool prepend, + bool isPayload, + bool preload) + : _parentPrim(parentPrim) + , _newPrimName(newPrimName) + , _newPrimType(newPrimType) + , _filePath(filePath) + , _primPath(primPath) + , _prepend(prepend) + , _isPayload(isPayload) + , _preload(preload) +{ +} + +void UsdUndoAddReferenceToNewPrimCommand::executeImplementation() +{ + if (!_parentPrim.IsValid()) + return; + + auto stage = _parentPrim.GetStage(); + if (!stage) + return; + + const std::string uniqueName = UsdUfe::uniqueChildName(_parentPrim, _newPrimName); + const SdfPath childPath = _parentPrim.GetPath().AppendChild(TfToken(uniqueName)); + + UsdPrim newPrim; + if (_newPrimType == "Class") { + newPrim = stage->CreateClassPrim(childPath); + } else { + TfToken typeToken = _newPrimType.empty() ? TfToken() : TfToken(_newPrimType); + newPrim = stage->DefinePrim(childPath, typeToken); + } + + if (!newPrim.IsValid()) { + TF_RUNTIME_ERROR( + "UsdUndoAddReferenceToNewPrimCommand: failed to create prim '%s'", + childPath.GetText()); + return; + } + + const SdfPath refPrimPath = _primPath.empty() ? SdfPath() : SdfPath(_primPath); + const auto listPos = getListPosition(_prepend); + + if (_isPayload) { + SdfPayload payload(_filePath, refPrimPath); + UsdPayloads primPayloads = newPrim.GetPayloads(); + PrimMetadataEditRouterContext ctx(newPrim, SdfFieldKeys->Payload); + primPayloads.AddPayload(payload, listPos); + if (_preload) { + stage->LoadAndUnload( + SdfPathSet { newPrim.GetPath() }, SdfPathSet {}, UsdLoadWithDescendants); + } else { + stage->LoadAndUnload(SdfPathSet {}, SdfPathSet { newPrim.GetPath() }); + } + } else { + SdfReference ref(_filePath, refPrimPath); + UsdReferences primRefs = newPrim.GetReferences(); + PrimMetadataEditRouterContext ctx(newPrim, SdfFieldKeys->References); + primRefs.AddReference(ref, listPos); + } +} + +} // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h b/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h new file mode 100644 index 000000000..5e43ef237 --- /dev/null +++ b/lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h @@ -0,0 +1,67 @@ +// +// Copyright 2026 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. +// +#ifndef USD_UFE_ADD_REFERENCE_TO_NEW_PRIM_COMMAND +#define USD_UFE_ADD_REFERENCE_TO_NEW_PRIM_COMMAND + +#include +#include + +#include +#include + +#include + +#include + +namespace USDUFE_NS_DEF { + +//! \brief Command that creates a new child prim and adds a reference (or payload) arc to +//! it in a single atomic undo/redo operation. +class USDUFE_PUBLIC UsdUndoAddReferenceToNewPrimCommand + : public UsdUndoableCommand +{ +public: + UsdUndoAddReferenceToNewPrimCommand( + const PXR_NS::UsdPrim& parentPrim, + const std::string& newPrimName, + const std::string& newPrimType, + const std::string& filePath, + const std::string& primPath, + bool prepend, + bool isPayload = false, + bool preload = false); + + USDUFE_DISALLOW_COPY_MOVE_AND_ASSIGNMENT(UsdUndoAddReferenceToNewPrimCommand); + +protected: + void executeImplementation() override; + +private: + static PXR_NS::UsdListPosition getListPosition(bool prepend); + + PXR_NS::UsdPrim _parentPrim; + std::string _newPrimName; + std::string _newPrimType; + std::string _filePath; + std::string _primPath; + bool _prepend; + bool _isPayload; + bool _preload; +}; + +} // namespace USDUFE_NS_DEF + +#endif /* USD_UFE_ADD_REFERENCE_TO_NEW_PRIM_COMMAND */ From cde11a7ee69dc59f46cee0735391f0a6036b0445 Mon Sep 17 00:00:00 2001 From: Anton Khelou Date: Wed, 29 Apr 2026 16:15:41 -0400 Subject: [PATCH 2/2] Adjust to use filename instead of default prim name as prim name --- lib/mayaUsd/ufe/MayaUsdContextOps.cpp | 32 +++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp index d5bc0e17a..885932ca5 100644 --- a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp +++ b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp @@ -776,30 +776,28 @@ Ufe::UndoableCommand::Ptr MayaUsdContextOps::doOpCmd(const ItemPath& itemPath) if (path.empty()) return nullptr; - // Inspect the referenced layer to determine the default prim name and type. - std::string newPrimName; + // Derive the new prim name from the referenced filename stem. + std::string stem = path; + const size_t slash = stem.find_last_of("/\\"); + if (slash != std::string::npos) + stem = stem.substr(slash + 1); + const size_t dot = stem.find_last_of('.'); + if (dot != std::string::npos) + stem = stem.substr(0, dot); + std::string newPrimName = TfMakeValidIdentifier(stem); + if (newPrimName.empty()) + newPrimName = "Reference"; + + // Inspect the referenced layer to determine the default prim type. std::string newPrimType; SdfLayerRefPtr layer = SdfLayer::FindOrOpen(path); if (layer && layer->HasDefaultPrim()) { - newPrimName = layer->GetDefaultPrim().GetString(); + const std::string defaultPrimName = layer->GetDefaultPrim().GetString(); SdfPrimSpecHandle primSpec - = layer->GetPrimAtPath(SdfPath("/" + newPrimName)); + = layer->GetPrimAtPath(SdfPath("/" + defaultPrimName)); if (primSpec) newPrimType = primSpec->GetTypeName().GetString(); } - // Fallback: when no default prim, derive the name from the filename stem. - if (newPrimName.empty()) { - std::string stem = path; - const size_t slash = stem.find_last_of("/\\"); - if (slash != std::string::npos) - stem = stem.substr(slash + 1); - const size_t dot = stem.find_last_of('.'); - if (dot != std::string::npos) - stem = stem.substr(0, dot); - newPrimName = TfMakeValidIdentifier(stem); - if (newPrimName.empty()) - newPrimName = "Reference"; - } const std::string refPrimPath = UsdMayaUtilFileSystem::getReferencedPrimPath(); const bool asRef = UsdMayaUtilFileSystem::wantReferenceCompositionArc();