Created
December 27, 2023 17:23
-
-
Save BigRoy/415669c52de00a0b31e3969ed3fc2417 to your computer and use it in GitHub Desktop.
Create Default Assembly Rig Setup as shown in How RISE VFX Reached New Creative Heights with USD in Maya talk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Create Default Assembly Rig Setup as shown in How RISE VFX Reached New Creative Heights with USD in Maya talk | |
See: https://youtu.be/8UIW-g1_heg?t=1902 | |
""" | |
import pxr | |
import ufe | |
import pymel.core as pm | |
from mayaUsd import lib as mayaUsdLib | |
USD_SCHEMA = "UsdSchemaBase" | |
def create_default_assembly_rig_setup(assembly_rig_stage_shape, rig_grp, stage_driver_grp): | |
"""Create default assembly rig setup. | |
Args: | |
assembly_rig_stage_shape (pm.PyNode): Maya USD Proxy Shape to transform. | |
This should reference a USD file with a default prim defined. | |
rig_grp (pm.PyNode): Maya group that should act as the rig group | |
stage_driver_grp (pm.PyNode): Group in which the locator should be | |
placed that can be moved around. | |
""" | |
stage = mayaUsdLib.GetPrim(assembly_rig_stage_shape.name(long=True)).GetStage() | |
# create default control setup | |
grp = pm.group(name='root_001_TRN', empty=True) | |
pm.group(name="root_001_OFFSET") | |
root_grp = pm.group(name="root_001_GRP") | |
root_ctrl = pm.circle(c=(0, 0, 0), normal=(0, 1, 0), sweep=360, radius=10, degree=3, ut=0, tol=0.01, s=8, constructionHistory=False, name="root_001_CTRL")[0] | |
root_ctrl.overrideEnabled.set(True) | |
root_ctrl.overrideColor.set(17) | |
pm.parent(root_ctrl, grp) | |
# create default control setup | |
grp = pm.group(name='rootOffset_001_TRN', empty=True) | |
pm.group(name="rootOffset_001_OFFSET") | |
root_offset_grp = pm.group(name="rootOffset_001_GRP") | |
root_offset_ctrl = pm.circle(c=(0, 0, 0), normal=(0, 1, 0), sweep=360, radius=10, degree=3, ut=0, tol=0.01, s=8, constructionHistory=False, name="rootOffset_001_CTRL")[0] | |
pm.parent(root_offset_ctrl, grp) | |
pm.parent(root_offset_grp, grp) | |
# parent control setup under rig group | |
pm.parent(root_grp, rig_grp) | |
pm.select(clear=True) | |
# create the stage root driver as an locator and parent under driver group | |
locator = pm.spaceLocator('root_LOC') | |
pm.parent(locator, stage_driver_grp) | |
pm.parentConstraint(root_offset_ctrl, locator, maintainOffset=False) | |
pm.scaleConstraint(root_offset_ctrl, locator) | |
# get default prim path, and build selection string to stage default prim | |
root_prim_path = str(stage.GetDefaultPrim().GetPath()) | |
stage_selection = "{}.{}".format( | |
assembly_rig_stage_shape.name(long=True), root_prim_path | |
) | |
# create connection to default/root prim | |
pm.select([stage_selection, locator]) | |
def connect_maya_node_to_usd_prim(translate_op=True, rotate_op=True, scale_op=True): | |
"""Select two objects, one xformable prim in Maya USD Proxy and a maya native transform | |
Then running this function will connect the maya transform to the xformable prim as | |
driven transforms. | |
""" | |
global_selection = ufe.GlobalSelection.get() | |
ufe_object = global_selection.front() | |
if USD_SCHEMA not in ufe_object.ancestorNodeTypes(): | |
return | |
stage_path, prim_path = mayaUsdLib.proxyAccessor.getDagAndPrimFromUfe(ufe_object) | |
stage_shape = pm.PyNode(stage_path) | |
stage = mayaUsdLib.GetPrim(stage_path).GetStage() | |
prim = stage.GetPrimAtPath(prim_path) | |
maya_node = pm.ls(selection=True)[0] | |
x_formable = pxr.UsdGeom.Xformable(prim) | |
if translate_op and not prim.GetAttribute("xformOp:translate").IsValid(): | |
translate = x_formable.AddTranslateOp() | |
translate.Set(pxr.Gf.Vec3f([0, 0, 0])) | |
if rotate_op and not prim.GetAttribute("xformOp:rotateXYZ").IsValid(): | |
rotate = x_formable.AddRotateXYZOp() | |
rotate.Set(pxr.Gf.Vec3f([0, 0, 0])) | |
if scale_op and not prim.GetAttribute("xformOp:scale").IsValid(): | |
scale = x_formable.AddScaleOp() | |
scale.Set(pxr.Gf.Vec3f([1, 1, 1])) | |
reset_set = ["!resetXformStack!"] | |
if translate_op: | |
reset_set.append("xformOp:translate") | |
if rotate_op: | |
reset_set.append("xformOp:rotateXYZ") | |
if scale_op: | |
reset_set.append("xformOp:scale") | |
prim.GetAttribute("xformOpOrder").Set(reset_set) | |
for src_attr, target_attr_name in { | |
maya_node.translate: "xformOp:translate", | |
maya_node.rotate: "xformOp:rotateXYZ", | |
maya_node.scale: "xformOp:scale", | |
}.items(): | |
accessor = mayaUsdLib.proxyAccessor.getOrCreateAccessPlug(ufeObject=ufe_object, usdAttrName=target_attr_name) | |
accessor_attr = stage_shape.attr(accessor) | |
pm.connectAttr(src_attr, accessor_attr, force=True) |
Just connect a maya native object to control transform of an object inside Maya USD Proxy shape, without pymel:
"""Create Default Assembly Rig Setup as shown in How RISE VFX Reached New Creative Heights with USD in Maya talk
See: https://youtu.be/8UIW-g1_heg?t=1902
"""
import pxr
import ufe
from maya import cmds
from mayaUsd import lib as mayaUsdLib
from mayaUsd.lib import proxyAccessor as pa
USD_SCHEMA = "UsdSchemaBase"
def connect_maya_node_to_usd_prim(
maya_node,
translate_op=True,
rotate_op=True,
scale_op=True):
"""Select two objects, one xformable prim in Maya USD Proxy and a maya native transform
Then running this function will connect the maya transform to the xformable prim as
driven transforms.
"""
global_selection = ufe.GlobalSelection.get()
ufe_object = global_selection.front()
if USD_SCHEMA not in ufe_object.ancestorNodeTypes():
return
stage_path, prim_path = mayaUsdLib.proxyAccessor.getDagAndPrimFromUfe(
ufe_object)
stage = mayaUsdLib.GetPrim(stage_path).GetStage()
prim = stage.GetPrimAtPath(prim_path)
x_formable = pxr.UsdGeom.Xformable(prim)
if translate_op and not prim.GetAttribute("xformOp:translate").IsValid():
translate = x_formable.AddTranslateOp()
translate.Set(pxr.Gf.Vec3f([0, 0, 0]))
if rotate_op and not prim.GetAttribute("xformOp:rotateXYZ").IsValid():
rotate = x_formable.AddRotateXYZOp()
rotate.Set(pxr.Gf.Vec3f([0, 0, 0]))
if scale_op and not prim.GetAttribute("xformOp:scale").IsValid():
scale = x_formable.AddScaleOp()
scale.Set(pxr.Gf.Vec3f([1, 1, 1]))
reset_set = ["!resetXformStack!"]
if translate_op:
reset_set.append("xformOp:translate")
if rotate_op:
reset_set.append("xformOp:rotateXYZ")
if scale_op:
reset_set.append("xformOp:scale")
prim.GetAttribute("xformOpOrder").Set(reset_set)
for src_attr, target_attr_name in {
f"{maya_node}.translate": "xformOp:translate",
f"{maya_node}.rotate": "xformOp:rotateXYZ",
f"{maya_node}.scale": "xformOp:scale",
}.items():
accessor = pa.getOrCreateAccessPlug(ufeObject=ufe_object,
usdAttrName=target_attr_name)
accessor_attr = f"{stage_path}.{accessor}"
cmds.connectAttr(src_attr, accessor_attr, force=True)
node = cmds.ls(selection=True)[0]
connect_maya_node_to_usd_prim(node)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Export Maya USD Proxy Shape animation in layer with proxy accesor via session layer
Export the animation in a MayaUsdProxyShape layer that is done through a connected proxy accessor, as shown by Rise VFX here. This uses the fact that stepping through the timeline writes/caches the proxy accessor's time samples into the stage's session layer.
Pros and cons
Pros
Cons
MayaReference
pulled dag paths metadata basically meaning that clearing the session layer will "break" any currently loaded references inside your USD stage. To avoid that it'd be better to only temporarily clear the layer during the export and then "revert" the original state after. This is trivial to implement however.