Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions lib/mayaUsd/ufe/MayaUsdContextOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <usdUfe/ufe/UsdSceneItem.h>
#include <usdUfe/ufe/UsdUndoAddPayloadCommand.h>
#include <usdUfe/ufe/UsdUndoAddReferenceCommand.h>
#include <usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h>
#include <usdUfe/ufe/UsdUndoClearPayloadsCommand.h>
#include <usdUfe/ufe/UsdUndoClearReferencesCommand.h>
#include <usdUfe/ufe/UsdUndoMaterialCommands.h>
Expand All @@ -38,7 +39,9 @@
#include <pxr/base/tf/stringUtils.h>
#include <pxr/pxr.h>
#include <pxr/usd/sdf/fileFormat.h>
#include <pxr/usd/sdf/layer.h>
#include <pxr/usd/sdf/path.h>
#include <pxr/usd/sdf/primSpec.h>
#include <pxr/usd/sdr/registry.h>
#include <pxr/usd/usd/common.h>
#include <pxr/usd/usd/prim.h>
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -757,7 +763,51 @@ 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;

// Derive the new prim name from the referenced filename stem.
std::string stem = path;
const size_t slash = stem.find_last_of("/\\");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should that cover \ and /. , maybe use the filesystem util maya-usd uses?

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())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that even possible? TfMakeValidIdentifier can return an empty string? probably not

newPrimName = "Reference";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont you need to check for name conflicts?

// Inspect the referenced layer to determine the default prim type.
std::string newPrimType;
SdfLayerRefPtr layer = SdfLayer::FindOrOpen(path);
if (layer && layer->HasDefaultPrim()) {
const std::string defaultPrimName = layer->GetDefaultPrim().GetString();
SdfPrimSpecHandle primSpec
= layer->GetPrimAtPath(SdfPath("/" + defaultPrimName));
if (primSpec)
newPrimType = primSpec->GetTypeName().GetString();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and if there's no default prim in there what happens, it explodes?

}

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<UsdUfe::UsdUndoAddReferenceToNewPrimCommand>(
prim(), newPrimName, newPrimType, path, refPrimPath, prepend, !asRef, preload);

} else if (itemPath[1] == kAddRefOrPayloadItem) {
if (!_prepareUSDReferenceTargetLayer(prim()))
return nullptr;

Expand Down
2 changes: 2 additions & 0 deletions lib/usdUfe/ufe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ target_sources(${PROJECT_NAME}
UsdUndoAddPayloadCommand.cpp
UsdUndoAddRefOrPayloadCommand.cpp
UsdUndoAddReferenceCommand.cpp
UsdUndoAddReferenceToNewPrimCommand.cpp
UsdUndoClearPayloadsCommand.cpp
UsdUndoClearReferencesCommand.cpp
UsdUndoCreateGroupCommand.cpp
Expand Down Expand Up @@ -199,6 +200,7 @@ set(HEADERS
UsdUndoAddPayloadCommand.h
UsdUndoAddRefOrPayloadCommand.h
UsdUndoAddReferenceCommand.h
UsdUndoAddReferenceToNewPrimCommand.h
UsdUndoClearPayloadsCommand.h
UsdUndoClearReferencesCommand.h
UsdUndoCreateGroupCommand.h
Expand Down
112 changes: 112 additions & 0 deletions lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.cpp
Original file line number Diff line number Diff line change
@@ -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 <usdUfe/ufe/Utils.h>
#include <usdUfe/utils/editRouterContext.h>

#include <pxr/base/tf/token.h>
#include <pxr/usd/sdf/path.h>
#include <pxr/usd/sdf/payload.h>
#include <pxr/usd/sdf/reference.h>
#include <pxr/usd/usd/payloads.h>
#include <pxr/usd/usd/references.h>
#include <pxr/usd/usd/stage.h>

namespace USDUFE_NS_DEF {

PXR_NAMESPACE_USING_DIRECTIVE

USDUFE_VERIFY_CLASS_SETUP(
UsdUndoableCommand<Ufe::UndoableCommand>,
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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that a real workflow, to reference a class prim into a new class prim? seems weird. Having a default class prim seems weird as well, dunno what that means

} else {
TfToken typeToken = _newPrimType.empty() ? TfToken() : TfToken(_newPrimType);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont htink empty type is a good default

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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume below here is like the add reference command? Would it not be simpler to just have a composite command and reuse the add reference command we already have?

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
67 changes: 67 additions & 0 deletions lib/usdUfe/ufe/UsdUndoAddReferenceToNewPrimCommand.h
Original file line number Diff line number Diff line change
@@ -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 <usdUfe/base/api.h>
#include <usdUfe/ufe/UsdUndoableCommand.h>

#include <pxr/usd/usd/common.h>
#include <pxr/usd/usd/prim.h>

#include <ufe/undoableCommand.h>

#include <string>

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<Ufe::UndoableCommand>
{
public:
UsdUndoAddReferenceToNewPrimCommand(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other add ref command is exposed to python

using This = UsdUfe::UsdUndoAddReferenceCommand;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useful if only for tests :

cmd = usdUfe.AddReferenceCommand(prim, refFile, True)

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 */