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-123720 - As a user, I'd like Display Layer membership to survive the 'Edit As Maya Data' workflow #2628

Merged
merged 10 commits into from
Oct 14, 2022
4 changes: 4 additions & 0 deletions lib/mayaUsd/fileio/primUpdaterContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <mayaUsd/base/api.h>
#include <mayaUsd/fileio/primUpdaterArgs.h>
#include <mayaUsd/ufe/Utils.h>

#include <pxr/base/tf/hashmap.h>
#include <pxr/pxr.h>
Expand Down Expand Up @@ -67,6 +68,9 @@ class UsdMayaPrimUpdaterContext
MAYAUSD_CORE_PUBLIC
MDagPath MapSdfPathToDagPath(const SdfPath& sdfPath) const;

mutable MayaUsd::ufe::ReplicateExtrasFromUSD _pullExtras;
Copy link
Collaborator

Choose a reason for hiding this comment

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

These mutable data members feel uncomfortable. Does the following make any sense:

  • initRecursive() writes into the map. Can we transform it into a static creator method?
  • processItem() and finalize() read from the map. They can be made const methods.
    That way, you can pass in the ReplicateExtras objects as const into the context, make them const data members, and provide const accessors to them, keeping the PrimUpdaterContext read-only.

Hope I didn't skip over some crucial detail...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Your plan will not fully work because ReplicateExtrasToUSD::processItem writes into the map.

Copy link
Collaborator

Choose a reason for hiding this comment

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

O.K., FromUSD works, ToUSD does not :) Let me think about it some more.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's a tweak to my suggestion to get it to work: everything as per above, but ReplicateExtrasToUSD::processItem() does a const cast if it needs to write into the map. Alternately, the map is mutable. I think this is acceptable, as the fact that processItem() is writing into the map is an invisible implementation detail. In that way, the prim updater context can appear to be conceptually read-only. What do you think?

mutable MayaUsd::ufe::ReplicateExtrasToUSD _pushExtras;

private:
const UsdTimeCode& _timeCode;
const UsdStageRefPtr _stage;
Expand Down
16 changes: 14 additions & 2 deletions lib/mayaUsd/fileio/primUpdaterManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ PullImportPaths pullImport(
Ufe::Path::Segments s { ps, Ufe::PathSegment(v.first, rtid, '/') };
Ufe::Path p(std::move(s));
objToUfePath.insert(ObjToUfePath::value_type(MObjectHandle(v.second), p));

context._pullExtras.processItem(p, v.second);
}
progressBar.advance();

Expand Down Expand Up @@ -631,6 +633,7 @@ PushCustomizeSrc pushExport(
auto usdPathToDagPathMap = std::make_shared<UsdPathToDagPathMap>();
for (const auto& v : writeJob.GetDagPathToUsdPathMap()) {
usdPathToDagPathMap->insert(UsdPathToDagPathMap::value_type(v.second, v.first));
context._pushExtras.processItem(v.first, v.second);
}

std::get<UsdPathToDagPathMapPtr>(pushCustomizeSrc) = usdPathToDagPathMap;
Expand Down Expand Up @@ -883,7 +886,7 @@ bool PrimUpdaterManager::mergeToUsd(
MString progStr(
VtDictionaryIsHolding<std::string>(userArgs, "rn_primName") ? "Caching to USD"
: "Merging to USD");
MayaUsd::ProgressBarScope progressBar(9, progStr);
MayaUsd::ProgressBarScope progressBar(10, progStr);
PushPullScope scopeIt(_inPushPull);

auto ctxArgs = VtDictionaryOver(userArgs, UsdMayaJobExportArgs::GetDefaultDictionary());
Expand Down Expand Up @@ -1020,6 +1023,9 @@ bool PrimUpdaterManager::mergeToUsd(
}
progressBar.advance();

context._pushExtras.finalize(MayaUsd::ufe::stagePath(context.GetUsdStage()));
progressBar.advance();

discardPullSetIfEmpty();

// Some updaters (like MayaReference) may be writing and changing the variant during merge.
Expand Down Expand Up @@ -1070,6 +1076,7 @@ bool PrimUpdaterManager::editAsMaya(const Ufe::Path& path, const VtDictionary& u

auto& scene = Ufe::Scene::instance();
auto ufeItem = Ufe::Hierarchy::createItem(path);
context._pullExtras.initRecursive(ufeItem);
if (!updaterArgs._copyOperation && TF_VERIFY(ufeItem))
scene.notify(Ufe::ObjectPreDelete(ufeItem));

Expand Down Expand Up @@ -1363,6 +1370,7 @@ bool PrimUpdaterManager::duplicate(

UsdMayaPrimUpdaterContext context(
srcProxyShape->getTime(), srcProxyShape->getUsdStage(), ctxArgs);
context._pullExtras.initRecursive(Ufe::Hierarchy::createItem(srcPath));
progressBar.advance();

pullImport(srcPath, srcPrim, context);
Expand All @@ -1377,7 +1385,7 @@ bool PrimUpdaterManager::duplicate(
return false;
}

MayaUsd::ProgressBarScope progressBar(6, "Duplicating to USD");
MayaUsd::ProgressBarScope progressBar(7, "Duplicating to USD");

auto ctxArgs = VtDictionaryOver(userArgs, UsdMayaJobExportArgs::GetDefaultDictionary());

Expand Down Expand Up @@ -1423,6 +1431,10 @@ bool PrimUpdaterManager::duplicate(
}
progressBar.advance();

std::string* rootRename = (dstChildName != srcRootPath.GetName()) ? &dstChildName : nullptr;
context._pushExtras.finalize(MayaUsd::ufe::stagePath(context.GetUsdStage()), rootRename);
progressBar.advance();

auto ufeItem = Ufe::Hierarchy::createItem(dstPath);
if (TF_VERIFY(ufeItem)) {
Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(ufeItem));
Expand Down
10 changes: 7 additions & 3 deletions lib/mayaUsd/render/vp2RenderDelegate/proxyRenderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,16 +818,20 @@ void ProxyRenderDelegate::_DirtyUsdSubtree(const UsdPrim& prim)
| HdChangeTracker::DirtyDisplayStyle | MayaUsdRPrim::DirtySelectionHighlight
| HdChangeTracker::DirtyMaterialId;

if (prim.IsA<UsdGeomGprim>()) {
if (prim.IsA<UsdGeomGprim>() && prim.IsActive()) {
auto indexPath = _sceneDelegate->ConvertCachePathToIndexPath(prim.GetPath());
changeTracker.MarkRprimDirty(indexPath, dirtyBits);
if (_renderIndex->HasRprim(indexPath)) {
changeTracker.MarkRprimDirty(indexPath, dirtyBits);
}
}

UsdPrimSubtreeRange range = prim.GetDescendants();
for (auto iter = range.begin(); iter != range.end(); ++iter) {
if (iter->IsA<UsdGeomGprim>()) {
auto indexPath = _sceneDelegate->ConvertCachePathToIndexPath(iter->GetPath());
changeTracker.MarkRprimDirty(indexPath, dirtyBits);
if (_renderIndex->HasRprim(indexPath)) {
changeTracker.MarkRprimDirty(indexPath, dirtyBits);
}
}
}
}
Expand Down
96 changes: 96 additions & 0 deletions lib/mayaUsd/ufe/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
#include <pxr/usd/usdGeom/tokens.h>
#include <pxr/usdImaging/usdImaging/delegate.h>

#ifdef MAYA_HAS_DISPLAY_LAYER_API
#include <maya/MFnDisplayLayer.h>
#include <maya/MFnDisplayLayerManager.h>
#endif

#include <maya/MFnDependencyNode.h>
#include <maya/MGlobal.h>
#include <maya/MObjectHandle.h>
Expand Down Expand Up @@ -895,5 +900,96 @@ std::vector<std::string> splitString(const std::string& str, const std::string&
return split;
}

void ReplicateExtrasFromUSD::initRecursive(Ufe::SceneItem::Ptr ufeItem)
{
auto node = Ufe::Hierarchy::hierarchy(ufeItem);
if (!node) {
return;
}

// Go through the entire hierarchy
for (auto child : node->children()) {
initRecursive(child);
}

#ifdef MAYA_HAS_DISPLAY_LAYER_API
// Prepare _displayLayerMap
MFnDisplayLayerManager displayLayerManager(
MFnDisplayLayerManager::currentDisplayLayerManager());

MObject displayLayerObj
= displayLayerManager.getLayer(Ufe::PathString::string(ufeItem->path()).c_str());
if (displayLayerObj.hasFn(MFn::kDisplayLayer)) {
MFnDisplayLayer displayLayer(displayLayerObj);
if (displayLayer.name() != "defaultLayer") {
_displayLayerMap[ufeItem->path()] = displayLayerObj;
}
}
#endif
}

void ReplicateExtrasFromUSD::processItem(const Ufe::Path& path, const MObject& mayaObject)
{
#ifdef MAYA_HAS_DISPLAY_LAYER_API
// Replicate display layer membership
auto it = _displayLayerMap.find(path);
if (it != _displayLayerMap.end() && it->second.hasFn(MFn::kDisplayLayer)) {
MDagPath dagPath;
if (MDagPath::getAPathTo(mayaObject, dagPath) == MStatus::kSuccess) {
MFnDisplayLayer displayLayer(it->second);
displayLayer.add(dagPath);

// In case display layer membership was removed from the USD prim that we are
// replicating, we want to restore it here to make sure that the prim will stay in its
// display layer on DiscardEdits
displayLayer.add(Ufe::PathString::string(path).c_str());
}
}
#endif
}

void ReplicateExtrasToUSD::processItem(const MDagPath& dagPath, const SdfPath& usdPath)
{
#ifdef MAYA_HAS_DISPLAY_LAYER_API
// Populate display layer membership map

// Since multiple dag paths may lead to a single USD path (like transform and node),
// we have to make sure we don't overwrite a non-default layer with a default one
bool displayLayerAssigned = false;
auto entry = _primToLayerMap.find(usdPath);
if (entry != _primToLayerMap.end() && entry->second.hasFn(MFn::kDisplayLayer)) {
MFnDisplayLayer displayLayer(entry->second);
displayLayerAssigned = (displayLayer.name() != "defaultLayer");
}

if (!displayLayerAssigned) {
MFnDisplayLayerManager displayLayerManager(
MFnDisplayLayerManager::currentDisplayLayerManager());
_primToLayerMap[usdPath] = displayLayerManager.getLayer(dagPath);
}
#endif
}

void ReplicateExtrasToUSD::finalize(const Ufe::Path& stagePath, const std::string* renameRoot)
{
#ifdef MAYA_HAS_DISPLAY_LAYER_API
// Replicate display layer membership
for (const auto& entry : _primToLayerMap) {
if (entry.second.hasFn(MFn::kDisplayLayer)) {
auto rootPath = MayaUsd::ufe::usdPathToUfePathSegment(entry.first);
if (renameRoot && !rootPath.empty()) {
*rootPath.begin() = Ufe::PathComponent(*renameRoot);
}

Ufe::Path::Segments segments { stagePath.getSegments()[0], rootPath };
Ufe::Path ufePath(std::move(segments));

MFnDisplayLayer displayLayer(entry.second);
displayLayer.add(Ufe::PathString::string(ufePath).c_str());
}
}
#endif
}

} // namespace ufe
} // namespace MAYAUSD_NS_DEF
26 changes: 26 additions & 0 deletions lib/mayaUsd/ufe/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,31 @@ Ufe::Selection recreateDescendants(const Ufe::Selection& src, const Ufe::Path& f
MAYAUSD_CORE_PUBLIC
std::vector<std::string> splitString(const std::string& str, const std::string& separators);

class ReplicateExtrasFromUSD
{
public:
// Prepares the replication operation for the subtree starting with the given scene item
void initRecursive(Ufe::SceneItem::Ptr);
// Replicates extra features from the USD item defined by 'path' to the maya object
void processItem(const Ufe::Path& path, const MObject& mayaObject);

private:
std::unordered_map<Ufe::Path, MObject> _displayLayerMap;
};

class ReplicateExtrasToUSD
{
public:
// Processes replication from a maya object defined by 'dagPath'
// to the usd item defined by 'usdPath'
void processItem(const MDagPath& dagPath, const PXR_NS::SdfPath& usdPath);
// Finalizes the replication operation to the USD stage defined by 'stagePath'
// with a possibility to rename the usd root node name to 'renameRoot'
void finalize(const Ufe::Path& stagePath, const std::string* renameRoot = nullptr);

private:
std::map<PXR_NS::SdfPath, MObject> _primToLayerMap;
};

} // namespace ufe
} // namespace MAYAUSD_NS_DEF
36 changes: 36 additions & 0 deletions test/lib/ufe/testDisplayLayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import mayaUsd.ufe as mayaUsdUfe
import mayaUsd.lib as mayaUsdLib

from usdUtils import createSimpleXformScene

from pxr import Usd, Kind

import unittest
Expand Down Expand Up @@ -343,6 +345,40 @@ def testDisplayLayerClear(self):
self.assertFalse(layer1.contains(self.CUBE1))
self.assertFalse(layer1.contains(self.INVALID_PRIM))

def testDisplayLayerEditAsMaya(self):
'''Display layer membership in Edit As Maya workflow.'''

(ps, xlateOp, xlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
_, _, _, _, _) = createSimpleXformScene()

# Add an item to a new display layer
cmds.createDisplayLayer(name='layer1', number=1, empty=True)
cmds.editDisplayLayerMembers('layer1', '|stage1|stageShape1,/A', noRecurse=True)

# Edit aPrim as Maya data.
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.canEditAsMaya(aUsdUfePathStr))
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(aUsdUfePathStr))

# Check display layer membership on the other side
layerObjs = cmds.editDisplayLayerMembers('layer1', query=True, **self.kwArgsEditDisplayLayerMembers)
self.assertTrue("|__mayaUsd__|AParent|A" in layerObjs)

# Create a new layer and put the item there
cmds.createDisplayLayer(name='layer2', number=1, empty=True)
cmds.editDisplayLayerMembers('layer2', "|__mayaUsd__|AParent|A", noRecurse=True)

# Merge edits back to USD.
aMayaItem = ufe.GlobalSelection.get().front()
aMayaPath = aMayaItem.path()
aMayaPathStr = ufe.PathString.string(aMayaPath)
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.mergeToUsd(aMayaPathStr))

# Check display layer membership back on the USD side
layerObjs = cmds.editDisplayLayerMembers('layer2', query=True, **self.kwArgsEditDisplayLayerMembers)
self.assertTrue('|stage1|stageShape1,/A' in layerObjs)

@unittest.skipUnless(mayaUtils.ufeSupportFixLevel() >= 3, "Requires Display Layer Ufe subtree invalidate fix.")
def testDisplayLayerSubtreeInvalidate(self):
# Create a scene with two variants.
Expand Down