I would like to programmatically add Maya References into the USD stage and later through code also be able to update these through Python code. What’s the simplest python API I can rely on to get that done?
If there are other better places to discuss Maya’s USD Plugin please point me in the right direction.
What did I try?
Let me report my findings with example code below. I’d love to know whether there are easier ways to do certain things here (I’m not that experienced with USD and definitely not with the Maya USD plugin).
Adding Maya Reference to mayaUsdProxyShape
through Python
Adding the Maya reference can be done through:
import mayaUsdAddMayaReference
mayaUsdAddMayaReference.createMayaReferencePrim(
ufe_path,
path,
namespace,
# you can add more of the (optional) arguments
# mayaReferencePrimName Name for the Maya Reference prim,
# groupPrim (3-tuple, group name, type and kind)
# variantSet (2-tuple, variant set name and variant name)
)
For newcomers, if you’re curious yourself about this function this might provide more info:
import mayaUsdAddMayaReference
help(mayaUsdAddMayaReference.createMayaReferencePrim)
print(f"Located at: {mayaUsdAddMayaReference.__file__}")
Find all Maya Reference prims in mayaUsdProxyShape
through Python
Then later I want to traverse the stage and find any MayaReferences, both those that are in Maya Edit mode and those that are not - this is where things start getting trickier because whenever the MayaReference Prim is switched to Edit as Maya mode the Prim itself doesn’t seem to exist anymore in the USD stage - or at least stage.Traverse()
doesn’t traverse over it.
Some related functions I have found are:
from maya import cmds
import mayaUsdUtils
# Detect whether a Maya node is a 'pulled' (=Edit as Maya) Maya Reference
is_pulled_maya_reference = mayaUsdUtils.isPulledMayaReference(node)
# Switch to "Edit as maya" mode
cmds.mayaUsdEditAsMaya(ufe_path)
# Switch (back) to USD mode discarding the current edits
cmds.mayaUsdDiscardEdits(node)
# Switch (back) to USD mode applying the edits
cmds.mayaUsdMergeToUsd(node)
Now given a mayaUsdProxyShape
I want to basically find all MayaReference nodes both in USD mode and Edit As Maya mode.
from maya import cmds
import mayaUsd.ufe
stage = mayaUsd.ufe.getStage(proxy_shape)
for prim in stage.Traverse():
if prim.GetTypeName() != "MayaReference":
continue
# `prim` now refers to a MayaReference prim that is not in edit mode
# but when in edit mode then the prim will not exist in the stage
So now I also need to get the associated pulled MayaReference nodes for the stage - the question now becomes what methods do I have available to do so? There must be some API managing these edit nodes, right? According to this documentation when merging edits to USD it clears the application data from the session layer. Which hints that the information I need might be available in the Stage’s session layer, and voila printing the session layer from the USD Layer Editor in Maya does list it:
// USD Layer identifier: anon:000001101FE32250:asset-session.usda
// Real Path:
// #usda 1.0
//
// over "root"
// {
// over "MayaReference1" (
// active = false
// customData = {
// dictionary Maya = {
// dictionary Pull = {
// string DagPath = "|__mayaUsd__|MayaReference1Parent|MayaReference1"
// }
// }
// }
// )
// {
// custom string MayaReferenceNodeName = "cubeRN"
// }
// }
//
So I figured I’d try stage.TraverseAll()
and it does indeed still list MayaReference
so I could go down that route to still find the relevant Maya USD Prim.
Question 1: How could I best/optimally find the Maya Reference Prims (and maya nodes) in both edit mode and USD mode?
Updating Maya Reference filepaths in mayaUsdProxyShape
through Python
For each MayaReference USD Prim I want to update the mayaReference
attribute to set a new reference filepath, basically replacing the reference filepath.
I could do:
attr = maya_reference_prim.GetAttribute("mayaReference")
attr.Set(new_filepath)
However, this would define a new opinion on the current edit target layer outside any VariantSet that the the initial loading might have created - but I’d prefer to update the value in the variant set (that is IF it was defined in a variant set; because it’s optional to do so through the Maya USD plugin.)
Question 2: How can I best find out what variantSet I might need to use to update the value?
Question 3: How can I update the value in the original edit target / original layer instead of the current edit target?
Here’s how far I got at 3):
from pxr import Sdf
# We want to update the authored opinion in the right place, e.g.
# within a VariantSet if it's authored there. We go through the
# PrimStack to find the first prim spec that authors an opinion
# on the 'mayaReference' attribute where we have permission to
# change it. This could technically mean we're altering it in
# layers that we might not want to (e.g. a published USD file?)
stack = prim.GetPrimStack()
for prim_spec in stack:
if "mayaReference" not in prim_spec.attributes:
# prim spec defines no opinion on mayaRefernce attribute?
continue
attr = prim_spec.attributes["mayaReference"]
if attr.permission != Sdf.PermissionPublic:
print(f"Not allowed to edit: {attr}")
continue
if filepath != attr.default:
print(f"Updating {attr.path} - {attr.default} -> {filepath}")
attr.default = filepath
# Attribute is either updated or already set to
# the value in that layer
return
# Just define in the current edit layer?
attr = prim.GetAttribute("mayaReference")
attr.Set(filepath)
But I’m unsure whether that’s even remotely in the direction of how this should be approached.
Related
Sorry for the lengthy post - I was hoping that by documenting the steps I took it might help other newcomers plus might make it easier to provide good feedback on my approach.