Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAYA-129169 correctly duplicate prim #3079

Merged
merged 3 commits into from
May 12, 2023
Merged
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
3 changes: 2 additions & 1 deletion lib/mayaUsd/nodes/proxyAccessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,8 @@ MStatus ProxyAccessor::stageChanged(const MObject& node, const UsdNotice::Object
if (_accessorInputItems.size() > 0) {
auto findInputItemFn = [this](const SdfPath& changedPath) -> Item* {
for (Item& item : _accessorInputItems) {
if (item.path == changedPath || item.path.AppendProperty(item.property) == changedPath) {
if (item.path == changedPath
|| item.path.AppendProperty(item.property) == changedPath) {
return &item;
}
}
Expand Down
17 changes: 9 additions & 8 deletions lib/mayaUsd/ufe/UsdHierarchy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include "private/Utils.h"

#include <mayaUsd/ufe/Utils.h>
#include <mayaUsdUtils/util.h>
#include <mayaUsd/utils/layers.h>

#include <pxr/base/tf/stringUtils.h>
#include <pxr/usd/sdf/copyUtils.h>
Expand Down Expand Up @@ -269,13 +269,14 @@ Ufe::AppendedChild UsdHierarchy::appendChild(const Ufe::SceneItem::Ptr& child)
std::string childName = uniqueChildName(fItem->prim(), child->path().back().string());

// Set up all paths to perform the reparent.
auto childPrim = usdChild->prim();
auto stage = childPrim.GetStage();
auto ufeSrcPath = usdChild->path();
auto usdSrcPath = childPrim.GetPath();
auto ufeDstPath = fItem->path() + childName;
auto usdDstPath = prim().GetPath().AppendChild(TfToken(childName));
SdfLayerHandle layer = MayaUsdUtils::defPrimSpecLayer(childPrim);
auto childPrim = usdChild->prim();
auto stage = childPrim.GetStage();
auto ufeSrcPath = usdChild->path();
auto usdSrcPath = childPrim.GetPath();
auto ufeDstPath = fItem->path() + childName;
auto usdDstPath = prim().GetPath().AppendChild(TfToken(childName));
auto primSpec = getDefiningPrimSpec(childPrim);
auto layer = primSpec ? primSpec->GetLayer() : SdfLayerHandle();
if (!layer) {
std::string err = TfStringPrintf("No prim found at %s", usdSrcPath.GetString().c_str());
throw std::runtime_error(err.c_str());
Expand Down
25 changes: 13 additions & 12 deletions lib/mayaUsd/ufe/UsdUndoDuplicateCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
#include <mayaUsd/ufe/Utils.h>
#include <mayaUsd/utils/editRouter.h>
#include <mayaUsd/utils/editRouterContext.h>
#include <mayaUsd/utils/layers.h>
#include <mayaUsd/utils/loadRules.h>
#ifdef UFE_V2_FEATURES_AVAILABLE
#include <mayaUsd/undo/UsdUndoBlock.h>
#include <mayaUsdUtils/MergePrims.h>
#endif
#include <mayaUsdUtils/util.h>

#include <pxr/base/tf/token.h>
#include <pxr/usd/sdf/copyUtils.h>
Expand Down Expand Up @@ -59,7 +59,9 @@ UsdUndoDuplicateCommand::UsdUndoDuplicateCommand(const UsdSceneItem::Ptr& srcIte
auto newName = uniqueChildName(parentPrim, srcPrim.GetName());
_usdDstPath = parentPrim.GetPath().AppendChild(TfToken(newName));

_srcLayer = MayaUsdUtils::getDefiningLayerAndPath(srcPrim).layer;
auto primSpec = getDefiningPrimSpec(srcPrim);
if (primSpec)
_srcLayer = primSpec->GetLayer();
}

UsdUndoDuplicateCommand::~UsdUndoDuplicateCommand() { }
Expand Down Expand Up @@ -101,20 +103,19 @@ void UsdUndoDuplicateCommand::execute()
// otherwise SdfCopySepc will fail.
SdfJustCreatePrimInLayer(_dstLayer, _usdDstPath.GetParentPath());

// Retrieve the layers where there are opinion and order them from weak
// to strong. We will copy the weakest opinions first, so that they will
// get over-written by the stronger opinions.
using namespace MayaUsdUtils;
std::vector<LayerAndPath> authLayerAndPaths = getAuthoredLayerAndPaths(prim);
// Retrieve the local layers around where the prim is defined and order them
// from weak to strong. That weak-to-strong order allows us to copy the weakest
// opinions first, so that they will get over-written by the stronger opinions.
SdfPrimSpecHandleVector authLayerAndPaths = getDefiningPrimStack(prim);
std::reverse(authLayerAndPaths.begin(), authLayerAndPaths.end());

MergePrimsOptions options;
options.verbosity = MergeVerbosity::None;
MayaUsdUtils::MergePrimsOptions options;
options.verbosity = MayaUsdUtils::MergeVerbosity::None;
bool isFirst = true;

for (const LayerAndPath& layerAndPath : authLayerAndPaths) {
const auto layer = layerAndPath.layer;
const auto path = layerAndPath.path;
for (const SdfPrimSpecHandle& layerAndPath : authLayerAndPaths) {
const auto layer = layerAndPath->GetLayer();
const auto path = layerAndPath->GetPath();
const bool result = isFirst
? SdfCopySpec(layer, path, _dstLayer, _usdDstPath)
: mergePrims(stage, layer, path, stage, _dstLayer, _usdDstPath, options);
Expand Down
11 changes: 11 additions & 0 deletions lib/mayaUsd/ufe/UsdUndoDuplicateCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ namespace MAYAUSD_NS_DEF {
namespace ufe {

//! \brief UsdUndoDuplicateCommand
//!
//! \details The USD duplicate command copies all opinions related the the USD prim
Copy link
Collaborator

Choose a reason for hiding this comment

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

small typo -> double "the"

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm okay if you fix this in a follow-up PR.

//! that are in the local layer stack of where the prim is first defined into
//! a single target layer, flattened.
//!
//! This means that over opinions in the session layer and any layers in the
//! same local layer stack anchored at the root layer are duplicated.
//!
//! It also means that opinion found in references and payloads are *not*
//! copied, but the references and payloads arcs are, so their opinions
//! are still taken into account.
#ifdef UFE_V4_FEATURES_AVAILABLE
class MAYAUSD_CORE_PUBLIC UsdUndoDuplicateCommand : public Ufe::SceneItemResultUndoableCommand
#else
Expand Down
196 changes: 165 additions & 31 deletions lib/mayaUsd/utils/layers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "layers.h"

#include <pxr/usd/pcp/layerStack.h>
#include <pxr/usd/usd/primCompositionQuery.h>

#include <deque>

Expand Down Expand Up @@ -116,44 +117,16 @@ void enforceMutedLayer(const PXR_NS::UsdPrim& prim, const char* command)
}
}

static SdfPrimSpecHandleVector _GetLocalPrimStack(const UsdPrim& prim)
{
SdfPrimSpecHandleVector primSpecs;

UsdStagePtr stage = prim.GetStage();
if (!stage)
return primSpecs;

// The goal is to avoid editing non-local layers. This issue is,
// for example, that a rename operation would fail when applied
// to a prim that references a show asset because the rename operation
// would be attempted on the reference and classes it inherits.
//
// Concrete example:
// - Create a test asset that inherits from one or more classes
// - Create a prim within a Maya Usd scene that references this asset
// - Attempt to rename the prim
// - Observe the failure due to Sdf policy

for (const SdfLayerHandle& layer : stage->GetLayerStack()) {
const SdfPrimSpecHandle primSpec = layer->GetPrimAtPath(prim.GetPath());
if (primSpec)
primSpecs.push_back(primSpec);
}

return primSpecs;
}

void applyToAllPrimSpecs(const UsdPrim& prim, const PrimSpecFunc& func)
{
const SdfPrimSpecHandleVector primStack = _GetLocalPrimStack(prim);
const SdfPrimSpecHandleVector primStack = getLocalPrimStack(prim);
for (const SdfPrimSpecHandle& spec : primStack)
func(prim, spec);
}

void applyToAllLayersWithOpinions(const UsdPrim& prim, PrimLayerFunc& func)
{
const SdfPrimSpecHandleVector primStack = _GetLocalPrimStack(prim);
const SdfPrimSpecHandleVector primStack = getLocalPrimStack(prim);
for (const SdfPrimSpecHandle& spec : primStack) {
const auto layer = spec->GetLayer();
func(prim, layer);
Expand All @@ -165,7 +138,7 @@ void applyToSomeLayersWithOpinions(
const std::set<SdfLayerRefPtr>& layers,
PrimLayerFunc& func)
{
const SdfPrimSpecHandleVector primStack = _GetLocalPrimStack(prim);
const SdfPrimSpecHandleVector primStack = getLocalPrimStack(prim);
for (const SdfPrimSpecHandle& spec : primStack) {
const auto layer = spec->GetLayer();
if (layers.count(layer) == 0)
Expand Down Expand Up @@ -243,4 +216,165 @@ SdfLayerHandle getStrongerLayer(
return getStrongerLayer(stage->GetRootLayer(), layer1, layer2);
}

SdfPrimSpecHandleVector
getPrimStackForLayers(const UsdPrim& prim, const SdfLayerHandleVector& layers)
{
SdfPrimSpecHandleVector primSpecs;

for (const SdfLayerHandle& layer : layers) {
const SdfPrimSpecHandle primSpec = layer->GetPrimAtPath(prim.GetPath());
if (primSpec)
primSpecs.push_back(primSpec);
}

return primSpecs;
}

SdfPrimSpecHandleVector getLocalPrimStack(const UsdPrim& prim)
{
// The goal is to avoid editing non-local layers. This issue is,
// for example, that a rename operation would fail when applied
// to a prim that references a show asset because the rename operation
// would be attempted on the reference and classes it inherits.
//
// Concrete example:
// - Create a test asset that inherits from one or more classes
// - Create a prim within a Maya Usd scene that references this asset
// - Attempt to rename the prim
// - Observe the failure due to Sdf policy

UsdStagePtr stage = prim.GetStage();
if (!stage)
return {};

return getPrimStackForLayers(prim, stage->GetLayerStack());
}

static void addSubLayers(const SdfLayerHandle& layer, std::set<SdfLayerHandle>& layers)
{
if (!layer)
return;

if (layers.count(layer) > 0)
return;

layers.insert(layer);
for (const std::string layerPath : layer->GetSubLayerPaths())
addSubLayers(SdfLayer::FindOrOpen(layerPath), layers);
}

static bool hasSubLayerInSet(const SdfLayerHandle& layer, const std::set<SdfLayerHandle>& layers)
{
if (!layer)
return false;

for (const std::string layerPath : layer->GetSubLayerPaths())
if (layers.count(SdfLayer::FindOrOpen(layerPath)) > 0)
return true;

return false;
}

SdfPrimSpecHandleVector getDefiningPrimStack(const UsdPrim& prim)
{
UsdStagePtr stage = prim.GetStage();
if (!stage)
return {};

const SdfPrimSpecHandle defPrimSpec = getDefiningPrimSpec(prim);
if (!defPrimSpec)
return {};

// Simple case: the prim is defined in the local layer stack of the stage.
{
const SdfLayerHandle defLayer = defPrimSpec->GetLayer();
const SdfPrimSpecHandleVector primSpecsInStageLayers = getLocalPrimStack(prim);
for (const SdfPrimSpecHandle& primSpec : primSpecsInStageLayers)
if (primSpec->GetLayer() == defLayer)
return primSpecsInStageLayers;
}

// Complex case: the prim is defined within a reference or payload.
//
// We need to build the layer stack of that payload or reference.
// Note that it could be a reference inside a reference, or a payload
// in a reference, or any deeper such nesting.
//
// We build the defining prim stack by going outward from the defining
// prim spec. We keep other prim spec if their layer is a parent or child
// of the layer that defines the prim. (The code beow starts from all the
// prim specs and removes the ones that are not in the layer hierarchy
// above and below the defining layer.)

// This keep tracks of layers we know are in the defining layer stack.
// We use this to identify other layer, for example identify a parent
// layer if one of its children in in this set.
std::set<SdfLayerHandle> definingLayers;
addSubLayers(defPrimSpec->GetLayer(), definingLayers);

SdfPrimSpecHandleVector primStack = prim.GetPrimStack();

const auto defPrimSpecPos = std::find(primStack.begin(), primStack.end(), defPrimSpec);
if (defPrimSpecPos == primStack.end())
return {};

const size_t defPrimSpecIndex = defPrimSpecPos - primStack.begin();

// Remove the sub-layers that are not in the local stack of the defining layer.
for (size_t index = defPrimSpecIndex + 1; index < primStack.size(); index += 1) {
const SdfPrimSpecHandle primSpec = primStack[index];

// If the prim spec layer is a sub-layer of the defining layer, then we keep
// it and add its children to the defining layers set.
SdfLayerHandle layer = primSpec->GetLayer();
if (definingLayers.count(layer) > 0) {
addSubLayers(layer, definingLayers);
continue;
}

// Otherwise, we remove the prim spec from the defining prim stack and
// decrease the index, since we erase it from the vector and the loop
// will increase the index.
primStack.erase(primStack.begin() + index);
index -= 1;
}

// Remove the parent layers that are not in the local stack of the defining layer.
for (size_t index = defPrimSpecIndex; index > 0;) {
index -= 1;
const SdfPrimSpecHandle primSpec = primStack[index];

// If the prim spec layer is a parent layer of the defining layer, then we keep
// it and add its children to the defining layers set.
SdfLayerHandle layer = primSpec->GetLayer();
if (hasSubLayerInSet(layer, definingLayers)) {
addSubLayers(layer, definingLayers);
continue;
}

// Otherwise, we remove the prim spec from the defining prim stack.
// We don't need to adjust the index since we are going backward.
primStack.erase(primStack.begin() + index);
}

return primStack;
}

SdfPrimSpecHandle getDefiningPrimSpec(const UsdPrim& prim)
{
SdfPrimSpecHandleVector primSpecs = prim.GetPrimStack();
for (SdfPrimSpecHandle primSpec : primSpecs) {
if (!primSpec)
continue;

const SdfSpecifier spec = primSpec->GetSpecifier();
if (spec != SdfSpecifierDef)
continue;

return primSpec;
}

return {};
}

} // namespace MAYAUSD_NS_DEF
30 changes: 30 additions & 0 deletions lib/mayaUsd/utils/layers.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,36 @@ PXR_NS::SdfLayerHandle getStrongerLayer(
const PXR_NS::SdfLayerHandle& layer2,
bool compareSessionLayers = false);

//! Return all layers in the given layers where there are opinions about the prim.
MAYAUSD_CORE_PUBLIC
PXR_NS::SdfPrimSpecHandleVector
getPrimStackForLayers(const PXR_NS::UsdPrim& prim, const PXR_NS::SdfLayerHandleVector& layers);

//! Return all local layers in the stage of the prim where there are opinions about the prim.
//
// The goal is to avoid editing non-local layers. This issue is,
// for example, that a rename operation would fail when applied
// to a prim that references a show asset because the rename operation
// would be attempted on the reference and classes it inherits.
//
// Concrete example:
// - Create a test asset that inherits from one or more classes
// - Create a prim within a Maya Usd scene that references this asset
// - Attempt to rename the prim
// - Observe the failure due to Sdf policy
MAYAUSD_CORE_PUBLIC
PXR_NS::SdfPrimSpecHandleVector getLocalPrimStack(const PXR_NS::UsdPrim& prim);

//! Return all layers and related paths in the layer stack where the prim is first defined.
// When the prim is in a reference, those paths will not be equal to the path of the input prim.
MAYAUSD_CORE_PUBLIC
PXR_NS::SdfPrimSpecHandleVector getDefiningPrimStack(const PXR_NS::UsdPrim& prim);

//! Return the layer and path where the prim is defined and the path relative to that layer.
// When the prim is in a reference, that path will not be equal to the path of the input prim.
MAYAUSD_CORE_PUBLIC
PXR_NS::SdfPrimSpecHandle getDefiningPrimSpec(const PXR_NS::UsdPrim& prim);

} // namespace MAYAUSD_NS_DEF

#endif
Loading