Skip to content

Commit

Permalink
Merge pull request #3079 from Autodesk/bailp/MAYA-129169/dup-copy-loc…
Browse files Browse the repository at this point in the history
…al-layer-stack

MAYA-129169 correctly duplicate prim
  • Loading branch information
seando-adsk authored May 12, 2023
2 parents 65707a8 + f62e72f commit 1769e34
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 182 deletions.
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
//! 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

0 comments on commit 1769e34

Please sign in to comment.