Skip to content

Instantly share code, notes, and snippets.

@BigRoy
Last active November 16, 2023 11:52
Show Gist options
  • Save BigRoy/44250f5d9fdba79d127ce96e88bcc197 to your computer and use it in GitHub Desktop.
Save BigRoy/44250f5d9fdba79d127ce96e88bcc197 to your computer and use it in GitHub Desktop.
USD API move prim spec including repathing relationships and connections to it and its children in a single Sdf Layer
from pxr import Sdf, Usd
LIST_ATTRS = ['addedItems', 'appendedItems', 'deletedItems', 'explicitItems',
'orderedItems', 'prependedItems']
def repath_properties(layer, old_path, new_path):
"""Re-path property relationship targets and attribute connections.
This will replace any relationship or connections from old path
to new path by replacing start of any path that matches the new path.
Args:
layer (Sdf.Layer): Layer to move prim spec path.
old_path (Union[Sdf.Path, str]): Source path to move from.
new_path (Union[Sdf.Path, str]): Destination path to move to.
Returns:
bool: Whether any re-pathing occurred for the given paths.
"""
old_path_str = str(old_path)
peformed_repath = False
def replace_in_list(spec_list):
"""Replace paths in SdfTargetProxy or SdfConnectionsProxy"""
for attr in LIST_ATTRS:
entries = getattr(spec_list, attr)
for i, entry in enumerate(entries):
entry_str = str(entry)
if entry == old_path or entry_str.startswith(
old_path_str + "/"):
# Repath
entries[i] = Sdf.Path(
str(new_path) + entry_str[len(old_path_str):])
peformed_repath = True
def repath(path):
spec = layer.GetObjectAtPath(path)
if isinstance(spec, Sdf.RelationshipSpec):
replace_in_list(spec.targetPathList)
if isinstance(spec, Sdf.AttributeSpec):
replace_in_list(spec.connectionPathList)
# Repath any relationship pointing to this src prim path
layer.Traverse("/", repath)
return peformed_repath
def move_prim_spec(layer, src_prim_path, dest_prim_path):
"""Move a PrimSpec and repath connections.
Note that the parent path of the destination must
exist, otherwise the namespace edit to that path
will fail.
Args:
layer (Sdf.Layer): Layer to move prim spec path.
src_prim_path (Union[Sdf.Path, str]): Source path to move from.
dest_prim_path (Union[Sdf.Path, str]): Destination path to move to.
Returns:
bool: Whether the move was successful
"""
src_prim_path = Sdf.Path(src_prim_path)
dest_prim_path = Sdf.Path(dest_prim_path)
dest_parent = dest_prim_path.GetParentPath()
dest_name = dest_prim_path.name
layer.GetPrimAtPath(dest_prim_path)
with Sdf.ChangeBlock():
reparent_edit = Sdf.NamespaceEdit.ReparentAndRename(
src_prim_path,
dest_parent,
dest_name,
-1
)
edit = Sdf.BatchNamespaceEdit()
edit.Add(reparent_edit)
if not layer.Apply(edit) and layer.GetPrimAtPath(src_prim_path):
print("Failed prim spec move")
return False
repath_properties(layer, src_prim_path, dest_prim_path)
return True
# Example usage in Maya to move /mtl to /asset/mtl for all
# maya usd proxy shapes in the scene in their current edit target layer
from maya import cmds
import mayaUsd
proxies = cmds.ls(type="mayaUsdProxyShape", long=True)
for proxy in proxies:
stage = mayaUsd.ufe.getStage("|world" + proxy)
layers = stage.GetLayerStack()
targets = cmds.mayaUsdEditTarget(proxy, query=True, editTarget=True)
target = targets[0] # edit target layer identifier
layer = next(
layer for layer in stage.GetLayerStack() if layer.identifier == target)
# Reparent and repath connections and relationships
move_prim_spec(layer, "/mtl", "/asset/mtl")
# Force viewport reset because sometimes viewport doesn't recognize
# the shader moved
cmds.ogs(reset=True)
@BigRoy
Copy link
Author

BigRoy commented Nov 15, 2023

As posted as a workaround for the issue in Maya USD unable to move shaders into asset structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment