Skip to content

context.SetPruneChildren(True) does not prevent reading children in python PrimReader #4355

@ilpoldo

Description

@ilpoldo

Describe the bug
Conventional examples of usd rigging workflows use assets the Usd description of the Maya Reference and the static/cached representation the asset in the composed stage are mutually exclusive variants in a given set.
I am currently prototyping a method to load a rig reference on-demand when using "editAsMaya" on the asset root (character_01) that does not require variant swaps or use of the Sdf API.

In this example the rigs to load are defined under a usd class:

#usda 1.0
(

)

def Scope "Assets" (
    kind = "assembly"
) {
    def Scope "characters" (
        kind = "group"
    ) {
        def Xform "character_01" (
            kind = "assembly"
        ) {
            def Xform "body" (
                kind = "component"
            ) {
                
            }
            def Xform "eyes" (
                kind = "component"
            ) {

            }
            def Xform "hair" (
                kind = "component"
            ) {

            }
            class "rig" (
            ) {
                def Scope "anim_rig" () {
                    def MayaReference "main" () {
                        asset mayaReference = @./my_rig.ma@
                        string mayaNamespace = "my_rig"
                    }
                }
            }
            
        }
    }
}
import mayaUsd.lib
from maya import cmds
from maya.api import OpenMaya
  
def resolve_rig_path(prim):
    maya_reference = prim.GetAttribute('mayaReference')
    rig_path = maya_reference.Get().resolvedPath if maya_reference else ""

    return rig_path


def get_rig_prims(prim):
    # hardcoded example
    rig_prim = prim.GetPrimAtPath('rig/anim_rig/main') 
    return [rig_prim] if rig_prim.IsValid() else [] 


class RigPrimReader(mayaUsd.lib.PrimReader):
 
    @classmethod
    def CanImport(cls, args, prim):
        return mayaUsdLib.PrimReader.ContextSupport.Supported
 
    def Read(self, context):

        prim = self._GetArgs().GetUsdPrim()
        print(f"[INFO] processing: {prim}")
        if context.GetPruneChildren():
            print(f'[INFO] processing children: {context.GetPruneChildren()}')
            return False

        rig_prims = get_rig_prims(prim)
        if not rig_prims:
            print(f"[INFO] skipping: {prim}")
            return False
        rig_network = cmds.createNode('transform', name=prim.GetName())

        for rig_prim in rig_prims:
            print(f"[INFO] Processing rig: {rig_prim}")
            rig_parent = cmds.createNode('transform', name=rig_prim.GetName())

            maya_file_path = resolve_rig_path(rig_prim)            
            ns_attr = rig_prim.GetAttribute("mayaNamespace")
            namespace = ns_attr.Get() if ns_attr and ns_attr.HasAuthoredValueOpinion() else rig_prim.GetName()
            
            print(f"[INFO] Loading rig: {maya_file_path} (ns={namespace})")
            try:
                ref_node = cmds.file(
                    maya_file_path,
                    reference=True,
                    namespace=namespace,
                    ignoreVersion=True,
                    mergeNamespacesOnClash=False,
                    options="v=0",
                    returnNewNodes=True
                )
            except RuntimeError as err:
                print("[WARN] Maya RuntimeError suppressed:", err)
            except Exception as e:
                print("[WARN] Other error suppressed:", e)
            finally:
                print("[INFO] Rig reference attempted; continuing pipeline.")            
                print("+_+_+_+_+_+_+_+_+_+_+_")
                print("[INFO] ref_node:", ref_node)
                all_nodes = cmds.ls(ref_node, type="transform")
                rig_top_nodes = [n for n in all_nodes if not cmds.listRelatives(n, parent=True)]
                print("[INFO] ref_node:", rig_top_nodes)
                if rig_top_nodes:
                    cmds.parent(rig_parent, rig_network)
                    cmds.parent(rig_top_nodes[0], rig_parent)
                    print(f'Read {prim.GetName()} - {rig_prim.GetName()} created')

        # parent under __mayaUsd__
        parent = context.GetMayaNode(prim.GetPath().GetParentPath(), True)
        parentDagPath = OpenMaya.MFnDagNode(parent).fullPathName()                
        print("[INFO] parentDagPath:", rig_network, parentDagPath)
        print("[INFO] DAG PATH Exist: ", cmds.objExists(parentDagPath))
        cmds.parent(rig_network, parentDagPath)
        selectionList = OpenMaya.MSelectionList()
        selectionList.add(rig_network)
        new_obj = selectionList.getDependNode(0)
        context.RegisterNewMayaNode(prim.GetPath().pathString, new_obj)
        # do not read children of character_01
        context.SetPruneChildren(True)
        print(f'[INFO] stopping at prim {prim}: {context.GetPruneChildren()}')
        return True

 
class RigPrimUpdater(mayaUsd.lib.PrimUpdater):
    @classmethod
    def sRegister(cls):
        print(f'Registering {cls.__qualname__}')
        mayaUsd.lib.PrimUpdater.Register(
            cls, "CustomRig", "transform", RigPrimUpdater.Supports.All.value
        )
        return True
 
    @classmethod
    def sUnregister(cls):
        print(f'Un-registering {cls.__qualname__}')
        mayaUsd.lib.PrimUpdater.Unregister(
            cls, "CustomRig", "transform", RigPrimUpdater.Supports.All.value
        )
        return True
 
    def canEditAsMaya(self):
        print('canEditAsMaya', self.__class__.__name__)
        return True
     
    def editAsMaya(self):
        print('[INFO] editAsMaya', self.__class__.__name__)
        return super(RigPrimUpdater, self).editAsMaya()


def register():
    RigPrimUpdater.sRegister()
    mayaUsd.lib.PrimReader.Register(RigPrimReader, "CustomRig")

register()                          

def test_edit_as_maya(stage_dag_path, prim_path):
    root_prim = mayaUsd.lib.GetPrim(stage_dag_path) # DAG path only
    stage = root_prim.GetStage()
    root_prim.GetPrimAtPath(prim_path).SetTypeName('CustomRig')

    ufepath = f'{stage_dag_path},{prim_path}'
    with mayaUsd.lib.OpUndoItemList():
       mayaUsd.lib.PrimUpdaterManager.editAsMaya(ufepath)

# read character_01 via RigPrimReader
test_edit_as_maya('|shot|shotShape', '/Assets/characters/character_01')

Steps to reproduce
Steps to reproduce the behavior:

  1. Create > USD > Stage From File...
  2. pick shot.usda from shot_and_rig.zip
  3. Run the code above

Expected behavior
The main rig is loaded under character_01 as expected.
The Maya transforms for /Assets/characters/character_01/body, eyes and hair should not be created, but they are (see screenshot).

Attachments

Image Image

shot_and_rig.zip

Specs (if applicable):

  • OS & version: macOS 15.4.1
  • Compiler & version Pre-Built version of the plug-in
  • Maya version Maya 2026.2
  • Maya USD commit SHA 0.33.0 (0.33.0_202507022244-1cbb7f7)
  • Pixar USD commit SHA 0.24.11

Additional context
The idea is to ensure that the geometric description of the asset is still accessible on the stage via usd APIs and edits don't have to be routed through composition arcs like variants or prim references.

Looking at the code for materialReader and testCustomRig.py it looked possible in principle.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions