Skip to content

Commit

Permalink
Merge pull request #3790 from jufrantz/issue3778/prim_metadata_edit_r…
Browse files Browse the repository at this point in the history
…outing

Prim metadata editRouting
  • Loading branch information
seando-adsk authored Oct 1, 2024
2 parents 2d5da8f + ca1ebb7 commit 1855db8
Show file tree
Hide file tree
Showing 21 changed files with 861 additions and 50 deletions.
72 changes: 71 additions & 1 deletion doc/EditRouting.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ data indexed by USD tokens (TfToken):
In theory, each edit routing operation could fill the context differently
and expect different data in the output dictionary. In practice many operations
share the same inputs and outputs. Currently, the operations can be divided in
three categories:
four categories:

- Simple commands
- Attributes
- Prim metadata
- Maya references

The following sections describe the input and output of each category. Each
Expand Down Expand Up @@ -121,6 +122,45 @@ def routeAttrToSessionLayer(context, routingData):
routingData['layer'] = prim.GetStage().GetSessionLayer().identifier
```

### Prim Metadata

Inputs:
- prim: the USD prim (UsdPrim) that is being affected.
- operation: the operation name (TfToken). Always 'primMetadata'.
- primMetadata: the metadata name (TfToken), e.g. "variantSelection"
- keyPath: the path of the edited key if the metadata is dict-valued (TfToken),
e.g. the variantSet name for "variantSelection" metadata, the key of a "customData".

Outputs:
- layer: the desired layer ID (text string) or layer handle (SdfLayerHandle).

On return, if the layer entry is empty, no routing is done and the current edit
target is used. Here is an example of a primMetadata edit router:

```Python
def routeVariantSelectionToSessionLayer(context, routingData):
'''
Edit router implementation for 'primMetadata' operations that routes
variant selections within variantSets named 'mySessionVariant' to the
session layer of the stage that contains the prim.
'''
prim = context.get('prim')
if prim is None:
return

metadataName = context.get('primMetadata')
if metadataName != "variantSelection":
return

variantSetName = context.get('keyPath')
if variantSetName != "mySessionVariant":
return

routingData['layer'] = prim.GetStage().GetSessionLayer().identifier

mayaUsd.lib.registerEditRouter('primMetadata', routeVariantSelectionToSessionLayer)
```

### Maya references

The maya reference edit routing is more complex than the other ones. It is
Expand Down Expand Up @@ -325,6 +365,7 @@ could be used:
import mayaUsd.lib

sessionAttributes = set(['visibility', 'radius'])
sessionVariantSets = set(['rigVariants', 'proxyVariants'])

def routeToSessionLayer(context, routingData):
'''
Expand Down Expand Up @@ -354,6 +395,27 @@ def routeAttrToSessionLayer(context, routingData):

routingData['layer'] = prim.GetStage().GetSessionLayer().identifier

def routeVariantSelectionToSessionLayer(context, routingData):
'''
Edit router implementation for 'primMetadata' operations that routes
some variantSelection to the session layer of the stage that contains the
prim.
'''
prim = context.get('prim')
if prim is None:
print('Prim not in context')
return

metadataName = context.get('primMetadata')
if metadataName != 'variantSelection':
return

variantSetName = context.get('keyPath')
if variantSetName not in sessionVariantSets:
return

routingData['layer'] = prim.GetStage().GetSessionLayer().identifier

def registerAttributeEditRouter():
'''
Register an edit router for the 'attribute' operation that routes to
Expand All @@ -368,8 +430,16 @@ def registerVisibilityEditRouter():
'''
mayaUsd.lib.registerEditRouter('visibility', routeToSessionLayer)

def registerPrimMetadataEditRouter():
'''
Register an edit router for the 'primMetadata' operation that routes to
the session layer.
'''
mayaUsd.lib.registerEditRouter('primMetadata', routeVariantSelectionToSessionLayer)

def registerEditRouters():
registerAttributeEditRouter()
registerVisibilityEditRouter()
registerPrimMetadataEditRouter()

```
82 changes: 56 additions & 26 deletions lib/mayaUsd/resources/ae/usdschemabase/ae_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .material_custom_control import MaterialCustomControl

import collections
import contextlib
import fnmatch
import re
import ufe
Expand Down Expand Up @@ -136,6 +137,15 @@ def onReplace(self, *args):
# Nothing needed here since we don't create any UI.
pass

@contextlib.contextmanager
def PrimCustomDataEditRouting(prim, *args):
'''
A context manager that activates prim customData editRouting.
'''
# Note: the edit router context must be kept alive in a variable.
ctx = mayaUsdUfe.PrimMetadataEditRouterContext(prim, Sdf.PrimSpec.CustomDataKey, *args)
yield

class MetaDataCustomControl(object):
'''Custom control for all prim metadata we want to display.'''
def __init__(self, item, prim, useNiceName):
Expand Down Expand Up @@ -220,11 +230,7 @@ def onReplace(self, *args):
# that case we don't need to update our controls since none will change.
pass

def refresh(self):
# PrimPath
cmds.textFieldGrp(self.primPath, edit=True, text=str(self.prim.GetPath()))

# Kind
def _refreshKind(self):
model = Usd.ModelAPI(self.prim)
primKind = model.GetKind()
if not primKind:
Expand All @@ -233,6 +239,13 @@ def refresh(self):
else:
cmds.optionMenuGrp(self.kind, edit=True, value=primKind)

def refresh(self):
# PrimPath
cmds.textFieldGrp(self.primPath, edit=True, text=str(self.prim.GetPath()))

# Kind
self._refreshKind()

# Active
cmds.checkBoxGrp(self.active, edit=True, value1=self.prim.IsActive())

Expand All @@ -246,8 +259,14 @@ def refresh(self):

def _onKindChanged(self, value):
with mayaUsdLib.UsdUndoBlock():
model = Usd.ModelAPI(self.prim)
model.SetKind(value)
try:
usdUfe.SetKindCommand(self.prim, value).execute()
except Exception as ex:
# Note: the command might not work because there is a stronger
# opinion or an editRouting prevention so update the option menu
self._refreshKind()
cmds.error(str(ex))


def _onActiveChanged(self, value):
with mayaUsdLib.UsdUndoBlock():
Expand Down Expand Up @@ -704,9 +723,15 @@ def onCreate(self, *args):
def onReplace(self, *args):
pass

def clear(self):
def _clearUseOutlinerColor(self):
cmds.checkBoxGrp(self.useOutlinerColor, edit=True, v1=False)

def _clearOutlinerColor(self):
cmds.colorSliderGrp(self.outlinerColor, edit=True, rgb=(0,0,0))

def clear(self):
self._clearUseOutlinerColor()
self._clearOutlinerColor()

def refresh(self):
try:
Expand All @@ -715,36 +740,44 @@ def refresh(self):
useOutlinerColor = self.item.getGroupMetadata(self.GROUP, self.USE_OUTLINER_COLOR)
if not useOutlinerColor.empty() and (useOutlinerColor.typeName() == 'bool'):
cmds.checkBoxGrp(self.useOutlinerColor, edit=True, v1=bool(useOutlinerColor))
else:
self._clearUseOutlinerColor()

outlinerColor = self.item.getGroupMetadata(self.GROUP, self.OUTLINER_COLOR)
if not outlinerColor.empty() and (outlinerColor.typeName() == "ufe.Vector3d"):
# Color is stored as double3 USD custom data.
clr = ufe.Vector3d(outlinerColor)
cmds.colorSliderGrp(self.outlinerColor, edit=True,
rgb=(clr.x(), clr.y(), clr.z()))
else:
self._clearOutlinerColor()
else:
# Get the custom data directly from USD.
useOutlinerColor = self.prim.GetCustomDataByKey(self.USE_OUTLINER_COLOR)
if useOutlinerColor is not None and isinstance(useOutlinerColor, bool):
cmds.checkBoxGrp(self.useOutlinerColor, edit=True, v1=useOutlinerColor)
else:
self._clearUseOutlinerColor()

outlinerColor = self.prim.GetCustomDataByKey(self.OUTLINER_COLOR)
if outlinerColor is not None and isinstance(outlinerColor, Gf.Vec3d):
# Color is stored as double3 USD custom data.
cmds.colorSliderGrp(self.outlinerColor, edit=True,
rgb=(outlinerColor[0], outlinerColor[1], outlinerColor[2]))
else:
self._clearOutlinerColor()
except:
self.clear()

def _updateTextColorChanged(self):
'''Update the text color custom data for this prim based on the values
set in the two fields.'''
currEditTarget = None
# Get the value of "Use Outliner Color" checkbox.
useTextColor = cmds.checkBoxGrp(self.useOutlinerColor, query=True, v1=True)
# Get the value of "Outliner Color" color slider.
rgb = cmds.colorSliderGrp(self.outlinerColor, query=True, rgbValue=True)
try:
if self.useMetadata:
useTextColor = cmds.checkBoxGrp(self.useOutlinerColor, query=True, v1=True)
rgb = cmds.colorSliderGrp(self.outlinerColor, query=True, rgbValue=True)

# Get ufe commands for the two metadata.
cmd1 = self.item.setGroupMetadataCmd(self.GROUP, self.USE_OUTLINER_COLOR, useTextColor)
ufeVec = ufe.Vector3d(rgb[0], rgb[1], rgb[2])
Expand All @@ -756,20 +789,17 @@ def _updateTextColorChanged(self):
else:
with mayaUsdLib.UsdUndoBlock():
# As initially decided write out the color custom data to the session layer.
stage = self.prim.GetStage()
currEditTarget = stage.GetEditTarget()
stage.SetEditTarget(stage.GetSessionLayer())

# Get the value of "Use Outliner Color" checkbox and set in custom data.
useTextColor = cmds.checkBoxGrp(self.useOutlinerColor, query=True, v1=True)
self.prim.SetCustomDataByKey(self.USE_OUTLINER_COLOR, useTextColor)

# Get the value of "Outliner Color" color slider and set in custom data.
rgb = cmds.colorSliderGrp(self.outlinerColor, query=True, rgbValue=True)
self.prim.SetCustomDataByKey(self.OUTLINER_COLOR, Gf.Vec3d(rgb[0], rgb[1], rgb[2]))
finally:
if currEditTarget is not None:
stage.SetEditTarget(currEditTarget)
# It still can be edit-routed as a 'primMetadata' operation.
fallbackLayer = self.prim.GetStage().GetSessionLayer()
with PrimCustomDataEditRouting(self.prim, self.USE_OUTLINER_COLOR, fallbackLayer):
self.prim.SetCustomDataByKey(self.USE_OUTLINER_COLOR, useTextColor)
with PrimCustomDataEditRouting(self.prim, self.OUTLINER_COLOR, fallbackLayer):
self.prim.SetCustomDataByKey(self.OUTLINER_COLOR, Gf.Vec3d(rgb[0], rgb[1], rgb[2]))
except Exception as ex:
# Note: the command might not work because there is a stronger
# opinion or an editRouting prevention so update the metadata controls.
self.refresh()
cmds.error(str(ex))

def _onUseOutlinerColorChanged(self, value):
self._updateTextColorChanged()
Expand Down
3 changes: 3 additions & 0 deletions lib/usdUfe/base/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@ namespace USDUFE_NS_DEF {
/* Stage received in the context of some router */ \
((Stage, "stage")) \
((EditTarget, "editTarget")) \
/* Metadata key path received in the context */ \
((KeyPath, "keyPath")) \
\
/* Routing operations */ \
\
((RouteParent, "parent")) \
((RouteDuplicate, "duplicate")) \
((RouteVisibility, "visibility")) \
((RouteAttribute, "attribute")) \
((RoutePrimMetadata, "primMetadata")) \
((RouteDelete, "delete")) \
((RouteTransform, "transform")) \
\
Expand Down
15 changes: 15 additions & 0 deletions lib/usdUfe/python/wrapCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <usdUfe/ufe/UsdUndoPayloadCommand.h>
#include <usdUfe/ufe/UsdUndoReloadRefCommand.h>
#include <usdUfe/ufe/UsdUndoSetDefaultPrimCommand.h>
#include <usdUfe/ufe/UsdUndoSetKindCommand.h>
#include <usdUfe/ufe/UsdUndoToggleActiveCommand.h>
#include <usdUfe/ufe/UsdUndoToggleInstanceableCommand.h>

Expand Down Expand Up @@ -67,6 +68,12 @@ UsdUfe::UsdUndoToggleInstanceableCommand* ToggleInstanceableCommandInit(const PX
return new UsdUfe::UsdUndoToggleInstanceableCommand(prim);
}

UsdUfe::UsdUndoSetKindCommand*
SetKindCommandInit(const PXR_NS::UsdPrim& prim, const PXR_NS::TfToken& kind)
{
return new UsdUfe::UsdUndoSetKindCommand(prim, kind);
}

UsdUfe::UsdUndoLoadPayloadCommand*
LoadPayloadCommandInit(const PXR_NS::UsdPrim& prim, PXR_NS::UsdLoadPolicy policy)
{
Expand Down Expand Up @@ -165,6 +172,14 @@ void wrapCommands()
.def("undo", &UsdUfe::UsdUndoToggleInstanceableCommand::undo)
.def("redo", &UsdUfe::UsdUndoToggleInstanceableCommand::redo);
}
{
using This = UsdUfe::UsdUndoSetKindCommand;
class_<This, boost::noncopyable>("SetKindCommand", no_init)
.def("__init__", make_constructor(SetKindCommandInit))
.def("execute", &UsdUfe::UsdUndoSetKindCommand::execute)
.def("undo", &UsdUfe::UsdUndoSetKindCommand::undo)
.def("redo", &UsdUfe::UsdUndoSetKindCommand::redo);
}
{
using This = UsdUfe::UsdUndoLoadPayloadCommand;
class_<This, boost::noncopyable>("LoadPayloadCommand", no_init)
Expand Down
10 changes: 10 additions & 0 deletions lib/usdUfe/python/wrapEditRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,14 @@ void wrapEditRouter()
using AttrThis = UsdUfe::AttributeEditRouterContext;
class_<AttrThis, boost::noncopyable>("AttributeEditRouterContext", no_init)
.def("__init__", make_constructor(AttributeEditRouterContextInit));

using PrimMdThis = UsdUfe::PrimMetadataEditRouterContext;
class_<PrimMdThis, boost::noncopyable>("PrimMetadataEditRouterContext", no_init)
.def(init<const PXR_NS::UsdPrim&, const PXR_NS::TfToken&>())
.def(init<const PXR_NS::UsdPrim&, const PXR_NS::TfToken&, const PXR_NS::TfToken&>())
.def(init<
const PXR_NS::UsdPrim&,
const PXR_NS::TfToken&,
const PXR_NS::TfToken&,
const PXR_NS::SdfLayerHandle&>());
}
16 changes: 12 additions & 4 deletions lib/usdUfe/ufe/SetVariantSelectionCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "SetVariantSelectionCommand.h"

#include <usdUfe/ufe/Utils.h>
#include <usdUfe/utils/editRouterContext.h>

#include <pxr/usd/usd/variantSets.h>

Expand Down Expand Up @@ -50,15 +51,20 @@ SetVariantSelectionCommand::SetVariantSelectionCommand(

void SetVariantSelectionCommand::redo()
{
const PXR_NS::TfToken metadataKeyPath(_varSet.GetName());

PrimMetadataEditRouterContext ctx(
_prim, PXR_NS::SdfFieldKeys->VariantSelection, metadataKeyPath);

std::string errMsg;
if (!UsdUfe::isPrimMetadataEditAllowed(
_prim,
PXR_NS::SdfFieldKeys->VariantSelection,
PXR_NS::TfToken(_varSet.GetName()),
&errMsg)) {
_prim, PXR_NS::SdfFieldKeys->VariantSelection, metadataKeyPath, &errMsg)) {
throw std::runtime_error(errMsg.c_str());
}

// Backup the destination layer for consistent undo.
_dstLayer = _prim.GetStage()->GetEditTarget().GetLayer();

// Make a copy of the global selection, to restore it on undo.
auto globalSn = Ufe::GlobalSelection::get();
_savedSn.replaceWith(*globalSn);
Expand All @@ -69,6 +75,8 @@ void SetVariantSelectionCommand::redo()

void SetVariantSelectionCommand::undo()
{
PrimMetadataEditRouterContext ctx(_prim.GetStage(), _dstLayer);

std::string errMsg;
if (!UsdUfe::isPrimMetadataEditAllowed(
_prim,
Expand Down
Loading

0 comments on commit 1855db8

Please sign in to comment.