Stage Traversal#

What is Stage Traversal?#

Stage traversal is the process of traversing the scenegraph of a stage with the purpose of querying or editing the scene data. We can traverse the scenegraph by iterating through child prims, accessing parent prims, and traversing the hierarchy to find specific prims of interest.

How Does It Work?#

Traversing stages works via the Usd.PrimRange iterator. Other methods like Usd.Stage.Traverse return a Usd.PrimRange iterator. The difference is that Usd.PrimRange takes a starting prim as an argument and Usd.Stage.Traverse starts it’s traversal from the stage’s pseudo-root.

Traversals operate by walking prim by prim in depth-first order. You can use predicates and Usd.PrimRange.PruneChildren to optimize your traversals if you know you don’t need to visit certain branches.

There are two traversal modes:

  • Default: Iterate over child prims

  • PreAndPostVisit: Iterate over the hierarchy and visit each prim twice, once when first encountering it, and then again when “exiting” the child hierarchy.

There are also predicates which can be used for pre-filtering the result:

  • Usd.PrimIsActive

    • If the active metadata is True

    • Analogous to Usd.Prim.IsActive()

  • Usd.PrimIsLoaded

    • If the (ancestor) payload is loaded

    • Analogous to Usd.Prim.IsLoaded()

  • Usd.PrimIsModel

    • If the kind is a sub kind of Kind.Tokens.model

    • Analogous to Usd.Prim.IsModel()

  • Usd.PrimIsGroup

    • If the kind is Kind.Tokens.group

    • Analogous to Usd.Prim.IsGroup()

  • Usd.PrimIsAbstract

    • If the prim specifier is Sdf.SpecifierClass

    • Analogous to Usd.Prim.IsAbstract()

  • Usd.PrimIsDefined

    • If the prim specifier is Sdf.SpecifierDef

    • Analogous to Usd.Prim.IsDefined()

Working With Python#

 1# This yields all active, loaded, defined, non-abstract prims on this stage depth-first
 2Usd.Stage.Traverse()
 3
 4# Traverse all prims in the stage
 5Usd.Stage.TraverseAll()
 6
 7# Predicates are combined used bitwise operators
 8predicate = Usd.PrimIsActive & Usd.PrimIsLoaded
 9
10# Traverse starting from the given prim and based on the predicate for filtering the traversal
11Usd.PrimRange(prim, predicate=predicate)
12
13# You must use iter() to invoke iterator methods like Usd.PrimRange.PruneChildren()
14it = iter(Usd.PrimRange.Stage(stage))
15for prim in it:
16    if prim.GetName() == "Environment":
17        prim_range.PruneChildren()  # Skip all children of "Environment"

Examples#

For reference, this the USDA for the stage that we will be using in the following examples:

        
    

Example 1: Traversing Through the Stage#

To traverse through the stage, we can use the Traverse() method. This traversal will yield prims that are active, loaded, defined, non-abstract on the stage in depth-first order.

 1# Import the Usd module from the pxr package
 2from pxr import Usd
 3
 4# Open the USD stage from the specified file
 5stage: Usd.Stage = Usd.Stage.Open("_assets/stage_traversal.usda")
 6
 7# Traverse and print the paths for the visited prims
 8for prim in stage.Traverse():
 9    # Print the path of each prim
10    print(prim.GetPath())
/World
/World/Box
/World/Box/Geometry
/World/Box/Geometry/Cube
/World/Box/Materials
/World/Box/Materials/BoxMat
/World/Environment
/World/Environment/SkyLight

Note how the prims were printed in depth-first order. All of the descendants of /World/Box appear before /World/Environment and its descendants.

Example 2: Traversing USD Content for Specific Prim Types#

For this practical example, we will traverse the stage to operate on specific prims based on their types.

We can filter based on the type of the prim. For example, we can check if the prim is of type scope or xform. To do this we pass the prim into the constructor method for the prim type we are interested in. For example,UsdGeom.Scope(prim) is equivalent to UsdGeom.Scope.Get(prim.GetStage(), prim.GetPath()) for a valid prim. If the prim’s type does not match, it will return an invalid prim.

 1# Import necessary modules from the pxr package
 2from pxr import Usd, UsdGeom
 3
 4# Open the USD stage from the specified file
 5stage: Usd.Stage = Usd.Stage.Open("_assets/stage_traversal.usda")
 6
 7scope_count = 0
 8xform_count = 0
 9# Traverse through each prim in the stage
10for prim in stage.Traverse():
11    # Check if the prim is of type Scope
12    if UsdGeom.Scope(prim):
13        scope_count += 1
14        print("Scope Type: ", prim.GetName())
15    # Check if the prim is of type Xform
16    elif UsdGeom.Xform(prim):
17        xform_count +=1
18        print("Xform Type: ", prim.GetName())
19
20print("Number of Scope prims: ", scope_count)
21print("Number of Xform prims: ", xform_count)
Xform Type:  World
Xform Type:  Box
Scope Type:  Geometry
Scope Type:  Materials
Scope Type:  Environment
Number of Scope prims:  3
Number of Xform prims:  2

Example 3: Traversing Through the Children of a Prim#

Using Traverse() can be a powerful tool, but for large stages, more efficient and targeted methods should be considered. A way to be more efficient and targeted is to traverse through the children of a prim.

If you need to work within a specific scope or hierarchy in the stage, you can perform a traversal starting from a particular prim. Let’s take a look at how we can traverse through the children of the default prim.

 1# Import the `Usd` module from the `pxr` package:
 2from pxr import Usd
 3
 4# Open the USD stage from the specified file:
 5stage: Usd.Stage = Usd.Stage.Open("_assets/stage_traversal.usda")
 6
 7# Get the default prim of the stage (/World in this case):
 8default_prim: Usd.Prim = stage.GetDefaultPrim()
 9
10# Iterate through all children of the default prim
11for child in default_prim.GetAllChildren():
12    # Print the path of each child prim
13    print(child.GetPath())
/World/Box
/World/Environment

Example 4: Traversing Using Usd.PrimRange#

Traverse() will return a UsdPrimRange object. UsdPrimRange exposes pre- and post- prim visitations allowing for a more involved traversals. It can also be used to perform actions such as pruning subtrees.

Let’s see an example of UsdPrimRange in use.

1# Import the Usd module from the pxr package
2from pxr import Usd
3
4# Open the USD stage from the specified file
5stage: Usd.Stage = Usd.Stage.Open("_assets/stage_traversal.usda")
6
7prim_range = Usd.PrimRange(stage.GetPrimAtPath("/World/Box"))
8for prim in prim_range:
9    print(prim.GetPath())
/World/Box
/World/Box/Geometry
/World/Box/Geometry/Cube
/World/Box/Materials
/World/Box/Materials/BoxMat

Note how only “/World/Box” and its descendants are printed.

There are other ways to use UsdPrimRange such as passing in predicates, you can find more information in the Using Usd.PrimRange in Python section of UsdPrimRange.

Key Takeaways#

Traversal is how we navigate the scenegraph and query scene data. Traversal can be made more efficient by limiting the number of prims it visits.