Skip to content

Commit

Permalink
EMSUSD-623 edit as Maya multiple variants
Browse files Browse the repository at this point in the history
This change adds support to edit-as-Maya a prim that is under a variant, with
each variation having a different Maya reference. That is, a single prim can
now be edited-as-Maya multiple times in parallel. This required major changes
on how those edited prim are tracked.

Edit-as-Maya authors data in the session layer related to the edited prim.
Previously, this data was authored outside any variant, which caused problems
when a prim had multiple variants. Now these data are authored inside the same
variant as the prim itself.

Additional helper functions:
- Add the getEditTargetForVariants function helper function to create the USD edit target targeting all the variants that affect a given prim.
- Add variant-specific pull info metadata in the pull-info helper functions.

Change to the orphan manager:
- Use the variant-targeting helper function when authoring the Maya reference custom attribute that holds the name of the Maya reference node.
- Support having multiple entries for a given UFE path.
- To differentiate between entries, the corresponding root Maya DAG path must be given in all functions to figure out which version of the prim we are dealing with.
- Fortunately, all callers always have on hand the DAG path of the root Maya node.
- Change all implementation function to iterate over these variations.
- Change the orphaned nodes serialization to support the extra data.
- Make the serialization format backward compatible by special-casing the usual case of having a single variation at a given edited UFE path.
- Add a log to tell the user when an edited prim is orphaned.

Changes to the prim updater manager:
- Ignore edited prim that are orphaned when determining if a prim has edited descendants.
- Adapt to changes to the orphaned manager by passing the Maya DAG path to the root of the edited nodes.

Changes to Maya reference translator (importer from USD to Maya):
- Use the variant-targeting helper function when authoring the pulled-prim metadata.
- Make many functions be purely in the implementation instead of declared private in the class.
- This allows better hiding of the implementation.
- This was also necessary to make the function callable from internal implementation functions.
- Renamed LoadMayaReference to CreateMayaReference to better reflect what the function does.
- Split the long update function into multiple function for clarity.
- When trying to reuse Maya reference nodes, verify that the referenced file is the one that was expected.
- This allows two prim with the same USD path but different references to work alongside each other.
- This happens when one prim with two variants each contain a Maya reference.
- Add more comments in the code to explain what is going on.
- Add a log to tell the user when a reference does not have the expected file path.
  • Loading branch information
pierrebai-adsk committed Sep 22, 2023
1 parent b48151b commit 41bfa0f
Show file tree
Hide file tree
Showing 10 changed files with 462 additions and 198 deletions.
150 changes: 103 additions & 47 deletions lib/mayaUsd/fileio/orphanedNodesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <pxr/usd/usd/editContext.h>

#include <maya/MFnDagNode.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <ufe/hierarchy.h>
#include <ufe/sceneSegmentHandler.h>
Expand Down Expand Up @@ -62,7 +63,7 @@ OrphanedNodesManager::Memento& OrphanedNodesManager::Memento::operator=(Memento&
return *this;
}

Ufe::Trie<OrphanedNodesManager::PullVariantInfo> OrphanedNodesManager::Memento::release()
OrphanedNodesManager::PulledPrims OrphanedNodesManager::Memento::release()
{
return std::move(_pulledPrims);
}
Expand All @@ -74,10 +75,11 @@ Ufe::Trie<OrphanedNodesManager::PullVariantInfo> OrphanedNodesManager::Memento::
namespace {

using PullVariantInfo = OrphanedNodesManager::PullVariantInfo;
using PullVariantInfos = OrphanedNodesManager::PullVariantInfos;
using VariantSetDescriptor = OrphanedNodesManager::VariantSetDescriptor;
using VariantSelection = OrphanedNodesManager::VariantSelection;
using PulledPrims = OrphanedNodesManager::PulledPrims;
using PulledPrimNode = Ufe::TrieNode<PullVariantInfo>;
using PulledPrimNode = OrphanedNodesManager::PulledPrimNode;

Ufe::PathSegment::Components trieNodeToPathComponents(PulledPrimNode::Ptr trieNode);
Ufe::Path trieNodeToPulledPrimUfePath(PulledPrimNode::Ptr trieNode);
Expand All @@ -102,11 +104,13 @@ void renameVariantInfo(
{
// Note: TrieNode has no non-const data() function, so to modify the
// data we must make a copy, modify the copy and call setData().
PullVariantInfo newVariantInfo = trieNode->data();
PullVariantInfos newVariantInfos = trieNode->data();

renameVariantDescriptors(newVariantInfo.variantSetDescriptors, oldPath, newPath);
for (auto& info : newVariantInfos) {
renameVariantDescriptors(info.variantSetDescriptors, oldPath, newPath);
}

trieNode->setData(newVariantInfo);
trieNode->setData(newVariantInfos);
}

void renamePullInformation(
Expand Down Expand Up @@ -141,8 +145,10 @@ void renamePullInformation(
}
}

const MDagPath& mayaPath = trieNode->data().editedAsMayaRoot;
TF_VERIFY(writePullInformation(pulledPath, mayaPath));
for (const PullVariantInfo& info : trieNode->data()) {
const MDagPath& mayaPath = info.editedAsMayaRoot;
TF_VERIFY(writePullInformation(pulledPath, mayaPath));
}
}

void recursiveRename(
Expand Down Expand Up @@ -209,18 +215,10 @@ OrphanedNodesManager::OrphanedNodesManager()
void OrphanedNodesManager::add(const Ufe::Path& pulledPath, const MDagPath& editedAsMayaRoot)
{
// Adding a node twice to the orphan manager is idem-potent. The manager was already
// racking that node.
if (_pulledPrims.containsDescendantInclusive(pulledPath))
// tracking that node.
if (_pulledPrims.containsDescendant(pulledPath))
return;

// Add the edited-as-Maya root to our pulled prims prefix tree. Also add the full
// configuration of variant set selections for each ancestor, up to the USD
// pseudo-root. Variants on the pulled path itself are ignored, as once
// pulled into Maya they cannot be changed.
if (_pulledPrims.containsDescendantInclusive(pulledPath)) {
TF_WARN("Trying to edit-as-Maya a descendant of an already edited prim.");
return;
}
if (pulledPath.runTimeId() != MayaUsd::ufe::getUsdRunTimeId()) {
TF_WARN("Trying to monitor a non-USD node for edit-as-Maya orphaning.");
return;
Expand All @@ -231,25 +229,37 @@ void OrphanedNodesManager::add(const Ufe::Path& pulledPath, const MDagPath& edit
auto ancestorPath = pulledPath.pop();
auto vsd = variantSetDescriptors(ancestorPath);

_pulledPrims.add(pulledPath, PullVariantInfo(editedAsMayaRoot, vsd));
PulledPrimNode::Ptr node = _pulledPrims.find(pulledPath);
if (node) {
PullVariantInfos infos = node->data();
infos.emplace_back(PullVariantInfo(editedAsMayaRoot, vsd));
node->setData(infos);
} else {
_pulledPrims.add(pulledPath, { PullVariantInfo(editedAsMayaRoot, vsd) });
}
}

OrphanedNodesManager::Memento OrphanedNodesManager::remove(const Ufe::Path& pulledPath)
OrphanedNodesManager::Memento
OrphanedNodesManager::remove(const Ufe::Path& pulledPath, const MDagPath& editedAsMayaRoot)
{
Memento oldPulledPrims(preserve());
TF_VERIFY(_pulledPrims.remove(pulledPath) != nullptr);
return oldPulledPrims;
}
Memento oldPulledPrims(preserve());
PulledPrimNode::Ptr node = _pulledPrims.find(pulledPath);
if (node) {
PullVariantInfos infos = node->data();
for (auto i = infos.begin(); i != infos.end(); ++i) {
if (i->editedAsMayaRoot == editedAsMayaRoot) {
infos.erase(i);
break;
}
}

const PullVariantInfo& OrphanedNodesManager::get(const Ufe::Path& pulledPath) const
{
const auto infoNode = _pulledPrims.find(pulledPath);
if (!infoNode || !infoNode->hasData()) {
static const PullVariantInfo empty;
return empty;
if (infos.size() > 0) {
node->setData(infos);
} else {
_pulledPrims.remove(pulledPath);
}
}

return infoNode->data();
return oldPulledPrims;
}

void OrphanedNodesManager::operator()(const Ufe::Notification& n)
Expand Down Expand Up @@ -429,7 +439,8 @@ OrphanedNodesManager::Memento OrphanedNodesManager::preserve() const

void OrphanedNodesManager::restore(Memento&& previous) { _pulledPrims = previous.release(); }

bool OrphanedNodesManager::isOrphaned(const Ufe::Path& pulledPath) const
bool OrphanedNodesManager::isOrphaned(const Ufe::Path& pulledPath, const MDagPath& editedAsMayaRoot)
const
{
auto trieNode = _pulledPrims.node(pulledPath);
if (!trieNode) {
Expand All @@ -442,15 +453,23 @@ bool OrphanedNodesManager::isOrphaned(const Ufe::Path& pulledPath) const
return false;
}

const PullVariantInfo& variantInfo = trieNode->data();
const std::vector<PullVariantInfo>& variantInfos = trieNode->data();

// If the pull parent is visible, the pulled path is not orphaned.
MDagPath pullParentPath = variantInfo.editedAsMayaRoot;
pullParentPath.pop();
for (const PullVariantInfo& variantInfo : variantInfos) {

MFnDagNode fn(pullParentPath);
auto visibilityPlug = fn.findPlug("visibility", /* tryNetworked */ true);
return !visibilityPlug.asBool();
if (!(variantInfo.editedAsMayaRoot == editedAsMayaRoot))
continue;

// If the pull parent is visible, the pulled path is not orphaned.
MDagPath pullParentPath = editedAsMayaRoot;
pullParentPath.pop();

MFnDagNode fn(pullParentPath);
auto visibilityPlug = fn.findPlug("visibility", /* tryNetworked */ true);
return !visibilityPlug.asBool();
}

return false;
}

namespace {
Expand Down Expand Up @@ -525,10 +544,21 @@ MStatus setNodeVisibility(const MDagPath& dagPath, bool visibility)
/* static */
bool OrphanedNodesManager::setOrphaned(const PulledPrimNode::Ptr& trieNode, bool orphaned)
{
TF_VERIFY(trieNode->hasData());
if (!trieNode->hasData())
return true;

const PullVariantInfo& variantInfo = trieNode->data();
for (const PullVariantInfo& variantInfo : trieNode->data())
setOrphaned(trieNode, variantInfo, orphaned);

return true;
}

/* static */
bool OrphanedNodesManager::setOrphaned(
const PulledPrimNode::Ptr& trieNode,
const PullVariantInfo& variantInfo,
bool orphaned)
{
// Note: the change to USD data must be done *after* changes to Maya data because
// the outliner reacts to UFE notifications received following the USD edits
// to rebuild the node tree and the Maya node we want to hide must have been
Expand All @@ -539,6 +569,21 @@ bool OrphanedNodesManager::setOrphaned(const PulledPrimNode::Ptr& trieNode, bool

const Ufe::Path pulledPrimPath = trieNodeToPulledPrimUfePath(trieNode);

std::string variantsNames;
for (const VariantSetDescriptor& varDesc : variantInfo.variantSetDescriptors) {
for (const VariantSelection& varSel : varDesc.variantSelections) {
if (variantsNames.empty())
variantsNames.append(" [");
else
variantsNames.append(";");
variantsNames.append(varSel.variantSetName);
variantsNames.append("=");
variantsNames.append(varSel.variantSelection);
}
if (!variantsNames.empty())
variantsNames.append("]");
}

// Note: if we are called due to the user deleting the stage, then the pulled prim
// path will be invalid and trying to add or remove information on it will
// fail, and cause spurious warnings in the script editor, so avoid it.
Expand All @@ -552,6 +597,12 @@ bool OrphanedNodesManager::setOrphaned(const PulledPrimNode::Ptr& trieNode, bool
}
}

TF_WARN(
"Edited-as-Maya prim \"%s%s\" %s.",
pulledPrimPath.string().c_str(),
variantsNames.c_str(),
orphaned ? "was orphaned and is now hidden" : "no longer orphaned and is now shown");

return true;
}

Expand Down Expand Up @@ -592,11 +643,14 @@ void OrphanedNodesManager::recursiveSwitch(
// tree state don't match, the pulled node must be made invisible.
// Inactivation must not be considered, as the USD pulled node is made
// inactive on pull, to avoid rendering it.
const auto& originalDesc = trieNode->data().variantSetDescriptors;
const auto currentDesc = variantSetDescriptors(ufePath.pop());
const bool variantSetsMatch = (originalDesc == currentDesc);
const bool orphaned = (pulledNode && !variantSetsMatch);
TF_VERIFY(setOrphaned(trieNode, orphaned));
const auto currentDesc = variantSetDescriptors(ufePath.pop());
const PullVariantInfos infos = trieNode->data();
for (const PullVariantInfo& variantInfo : infos) {
const auto& originalDesc = variantInfo.variantSetDescriptors;
const bool variantSetsMatch = (originalDesc == currentDesc);
const bool orphaned = (pulledNode && !variantSetsMatch);
TF_VERIFY(setOrphaned(trieNode, variantInfo, orphaned));
}
} else {
const bool isGatewayToUsd = Ufe::SceneSegmentHandler::isGateway(ufePath);
for (const auto& c : trieNode->childrenComponents()) {
Expand Down Expand Up @@ -626,8 +680,10 @@ OrphanedNodesManager::variantSetDescriptors(const Ufe::Path& p)
auto ancestor = Ufe::Hierarchy::createItem(path);
auto usdAncestor = std::static_pointer_cast<UsdUfe::UsdSceneItem>(ancestor);
auto variantSets = usdAncestor->prim().GetVariantSets();
auto setNames = variantSets.GetNames();
std::sort(setNames.begin(), setNames.end());
std::list<VariantSelection> vs;
for (const auto& vsn : variantSets.GetNames()) {
for (const auto& vsn : setNames) {
vs.emplace_back(vsn, variantSets.GetVariantSelection(vsn));
}
vsd.emplace_back(path, vs);
Expand Down
24 changes: 13 additions & 11 deletions lib/mayaUsd/fileio/orphanedNodesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class MAYAUSD_CORE_PUBLIC OrphanedNodesManager : public Ufe::Observer
std::list<VariantSetDescriptor> variantSetDescriptors;
};

using PullVariantInfos = std::vector<PullVariantInfo>;
using PulledPrims = Ufe::Trie<PullVariantInfos>;
using PulledPrimNode = Ufe::TrieNode<PullVariantInfos>;

/// \brief Entire state of the OrphanedNodesManager at a point in time, used for undo/redo.
class MAYAUSD_CORE_PUBLIC Memento
{
Expand All @@ -117,11 +121,11 @@ class MAYAUSD_CORE_PUBLIC OrphanedNodesManager : public Ufe::Observer
// Private, for opacity.
friend class OrphanedNodesManager;

Memento(Ufe::Trie<PullVariantInfo>&& pulledPrims);
Memento(PulledPrims&& pulledPrims);

Ufe::Trie<PullVariantInfo> release();
PulledPrims release();

Ufe::Trie<PullVariantInfo> _pulledPrims;
PulledPrims _pulledPrims;
};

// Construct an empty orphan manager.
Expand All @@ -138,11 +142,7 @@ class MAYAUSD_CORE_PUBLIC OrphanedNodesManager : public Ufe::Observer
// Remove the pulled path from the trie of pulled prims. Asserts that the
// path is in the trie. Returns a memento (see Memento Pattern) for undo
// purposes, to be used as argument to restore().
Memento remove(const Ufe::Path& pulledPath);

// Retrieve the variant information of a pulled prim.
// Returns an empty info if the prim was not tracked by the orphan manager.
const PullVariantInfo& get(const Ufe::Path& pulledPath) const;
Memento remove(const Ufe::Path& pulledPath, const MDagPath& editedAsMayaRoot);

// Preserve the trie of pulled prims into a memento.
Memento preserve() const;
Expand All @@ -158,10 +158,8 @@ class MAYAUSD_CORE_PUBLIC OrphanedNodesManager : public Ufe::Observer

// Return whether the Dag hierarchy corresponding to the pulled path is
// orphaned.
bool isOrphaned(const Ufe::Path& pulledPath) const;
bool isOrphaned(const Ufe::Path& pulledPath, const MDagPath& editedAsMayaRoot) const;

using PulledPrims = Ufe::Trie<PullVariantInfo>;
using PulledPrimNode = Ufe::TrieNode<PullVariantInfo>;
const PulledPrims& getPulledPrims() const { return _pulledPrims; }

private:
Expand All @@ -171,6 +169,10 @@ class MAYAUSD_CORE_PUBLIC OrphanedNodesManager : public Ufe::Observer
static void recursiveSwitch(const PulledPrimNode::Ptr& trieNode, const Ufe::Path& ufePath);

static bool setOrphaned(const PulledPrimNode::Ptr& trieNode, bool orphaned);
static bool setOrphaned(
const PulledPrimNode::Ptr& trieNode,
const PullVariantInfo& variantInfo,
bool orphaned);

// Member function to access private nested classes.
static std::list<VariantSetDescriptor> variantSetDescriptors(const Ufe::Path& path);
Expand Down
Loading

0 comments on commit 41bfa0f

Please sign in to comment.