USD (Universal Scene Description) is both an open source interchange format for arbitrary 3D scenes, as well as a runtime which provides a rich toolset for reading, writing, editing scenes.

These tutorials will help Python developers new to USD learn how to perform common tasks on the scene graph, as well as learn how to be more proficient in the USD documentation.

These tutorials are not meant to provide an introduction to 3D scene graphs and runtimes in general. Also, some experience with USD is beneficial but not required. If you have never encountered USD before, a good starting resource to get your hands dirty are the USD Tutorials from Pixar's USD site.

The tutorials are organized by topic rather than sequential updates to a single goal, so they do not need to be read in any specific order.

Opening USD Stages

Working with USD Stages is pretty straight forward, as most times everything is one function call away.

To load a USD file as a USD Stage you use Usd.Stage.Open(path):

from pxr import Usd

stage = Usd.Stage.Open('path_to_file.usd')

To create a new Stage use Usd.Stage.CreateNew(path):

from pxr import Usd

stage = Usd.Stage.CreateNew('a_new_stage.usd')  

To save a loaded Stage use Usd.Stage.Save(path)

from pxr import Usd

stage = Usd.Stage.Open('path_to_file.usd')
# do something to the stage
stage.Save()  

To export a stage to a new file, you can use Usd.Stage.Export(). This function allows you to transition between serialization formats (usda or usdc) as well, based on the file extension provided.

from pxr import Usd

stage = Usd.Stage.Open('a_usda_file.usda')
# do something to the stage
stage.Export('a_usdc_file.usdc')

Prims, Attributes and Metadata

Prims

Working with Prims is a more complicated since Prims are extremely powerful objects in USD. Prims are referenced by their path in the stage, which is a string in the form of /Prim/ChildPrim. / is a special prim known as the root prim in a stage.

To get a reference to a prim at a path use stage_ref.GetPrimAtPath(path):

from pxr import Usd

stage_ref = Usd.Stage.Open('stage.usda')

prim = stage_ref.GetPrimAtPath('/Prim')
print(prim.GetName()) # prints "Prim"
print(prim.GetPrimPath()) # prints "/Prim"

To define a new prim use stage_ref.DefinePrim(path):

from pxr import Usd

stage_ref = Usd.Stage.Open('stage.usda')

prim = stage_ref.DefinePrim('/UnTypedPrim')
print(prim.GetName()) # prints "UnTypedPrim"

To define a new prim with a type use stage_ref.DefinePrim(path, type_name) or you can use your Type's SomeType.Define(stage_ref, path) method:

from pxr import Usd, UsdGeom

stage_ref = Usd.Stage.Open('stage.usda')

prim = stage_ref.DefinePrim('/XformPrim', 'Xform')
# above we have a Usd.Prim, if we want to access all the Xform's types natively,
we need to get an Xform instance of our prim
xform = UsdGeom.Xform(prim)

print(xform.GetPath()) # prints "/XformPrim"

# it is often better to use the Define() method of your type right away, since
it returns your typed instance rather than a Usd.Prim instance

xform_faster = UsdGeom.Xform.Define(stage_ref, '/AnotherXformPrim')

To delete a prim from the current edit layer (please refer to the documentation about RemovePrim for details) you can use stage_ref.RemovePrim(path):

from pxr import Usd

stage_ref = Usd.Stage.Open('stage.usda')
prim = stage_ref.DefinePrim('/UnTypedPrim')

if stage_ref.RemovePrim('/UnTypedPrim'):
print('/UnTypedPrim removed')

# if you try to access the prim object, it will still reference path but it is expired
if (prim.IsValid()):
    print('{} is valid'.format(prim.GetName()))
else:
    print('{} is not valid'.format(prim.GetName()))
  
# the above will print "UnTypedPrim is not valid"

Attributes

Attributes are the workhorse of storing actual data inside a Prim. Attributes are often defined as part of Schemas to make it easier to access context-relevant data from within an instance of that Type.

For example, Xform typed Prims have an attribute called Purpose which is used to specify the purpose of an imageable prim. It contains one of the following values: [default, render, proxy, guide]

Now, you could get this attribute's value in two ways. One, as a generic prim_ref.GetAttribute(name) call, but you would have to know that the exact name of the attribute you want is "purpose", and you wouldn't be able to get any code completion in an IDE that way.

The other way is to use the Xform Schema's exposed function for getting the purpose, which is xform_ref.GetPurposeAttr(), which returns the same object, but will be typed in an IDE and does not depend on the underlying string name of the attribute.

Most often after you get an Attribute object, you will want to get the attribute's actual value or set it. That can be done with attribute_ref.Get() to retrieve the value, and attribute_ref.Set(value) to set the value.

Note! attribute_ref.Set(value) requires passing the correct type of value as well, refer to table in Working with USD Types below to see how to construct the appropriate type.

Let's see the code for getting an Attribute reference and getting its value:

from pxr import Usd, UsdGeom

stage_ref = Usd.Stage.Open('stage.usda')

# get a reference to the Xform instance as well as a generic Prim instance
xform_ref = UsdGeom.Xform.Define(stage_ref, '/XformPrim')
prim_ref = xform_ref.GetPrim()

# Get an attribute reference (not its value!)
purpose_from_xform_ref = xform_ref.GetPurposeAttr()
purpose_from_prim_ref = prim_ref.GetAttribute('purpose')

print(purpose_from_xform_ref == purpose_from_prim_ref) # prints True

# prints the actual attribute's value, in this case, one of [default, render,
proxy, guide], since it is the Xform's actual Purpose attribute
print(purpose_from_xform_ref.Get())

To create an attribute that isn't part of a Type's namespace (or it is, but you want to create the attribute "manually"), you must pass the attribute name and its type to prim_ref.CreateAttribute(name, type).

Otherwise, most Types expose a Set -style command, for example xform_ref.SetPurposeAttr(value).

The name of an Attribute is a string. The type system for Attributes (and USD in general) is explained below.

Working with USD Types

The USD API Documentation has a list of USD Basic Data Types that are used for Attribute types. Type Name are provided by the Sdf.ValueTypeNames module, which is a useful resource for reference.

In order to actually pass a valid value for the type for the CreateAttribute(name, type) function, it needs to be a reference to a Sdf.ValueTypeNames.TypeName object. There are three ways of getting these:

    1. The current values and how to construct an object that will function as that value are in a table here for convenience, please refer to the USD Basic Data Types page for an explanation on explicit definitions. [] denotes a list of types. The left column of this list was generated with the method from 1) above.

    Note: Gf.Vec3d/f/h instances below can be constructed with just a (float, float, float) tuple (and Vec2d/f/h , Vec4d/f/h as well), but the explicit declaration is passed below for completeness.

    Attribute Type -> Constructor Table

    Type Name Python Constructor
    Asset string
    AssetArray [string]
    Bool bool
    BoolArray [bool]
    Color3d Gf.Vec3d(float, float, float)
    Color3dArray [Gf.Vec3d(float, float, float)]
    Color3f Gf.Vec3f(float, float, float)
    Color3fArray [Gf.Vec3f(float, float, float)]
    Color3h Gf.Vec3h(float, float, float)
    Color3hArray [Gf.Vec3h(float, float, float)]
    Color4d Gf.Vec4d(float, float, float, float)
    Color4dArray [Gf.Vec4d(float, float, float, float)]
    Color4f Gf.Vec4f(float, float, float, float)
    Color4fArray [Gf.Vec4f(float, float, float, float)]
    Color4h Gf.Vec4h(float, float, float, float)
    Color4hArray [Gf.Vec4h(float, float, float, float)]
    Double float
    Double2 (float, float)
    Double2Array [(float, float)]
    Double3 (float, float, float)
    Double3Array [(float, float, float)]
    Double4 (float, float, float, float)
    Double4Array [(float, float, float, float)]
    DoubleArray [float]
    Float float
    Float2 (float, float)
    Float2Array [(float, float)]
    Float3 (float, float, float)
    Float3Array [(float, float, float)]
    Float4 (float, float, float, float)
    Float4Array [(float, float, float, float)]
    FloatArray [float]
    Frame4d float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float
    Frame4dArray [Gf.Matrix4d(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float)]
    Half float
    Half2 (float, float)
    Half2Array [(float, float)]
    Half3 (float, float, float)
    Half3Array [(float, float, float)]
    Half4 (float, float, float, float)
    Half4Array [(float, float, float, float)]
    HalfArray [float]
    Int int
    Int2 (int, int)
    Int2Array [(int, int)]
    Int3 (int, int, int)
    Int3Array [(int, int, int)]
    Int4 (int, int, int, int)
    Int4Array [(int, int, int, int)]
    Int64 long or int in Python3
    Int64Array [long] or [int] in Python3
    IntArray [int]
    Matrix2d float, float, float, float
    Matrix2dArray [Gf.Matrix2d(float, float, float, float)]
    Matrix3d float, float, float, float, float, float, float, float, float
    Matrix3dArray [Gf.Matrix3d(float, float, float, float, float, float, float, float, float)]
    Matrix4d float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float
    Matrix4dArray [Gf.Matrix4d(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float)]
    Normal3d Gf.Vec3d(float, float, float)
    Normal3dArray [Gf.Vec3d(float, float, float)]
    Normal3f Gf.Vec3f(float, float, float)
    Normal3fArray [Gf.Vec3f(float, float, float)]
    Normal3h Gf.Vec3h(float, float, float)
    Normal3hArray [Gf.Vec3h(float, float, float)]
    Point3d Gf.Vec3d(float, float, float)
    Point3dArray [Gf.Vec3d(float, float, float)]
    Point3f Gf.Vec3f(float, float, float)
    Point3fArray [Gf.Vec3f(float, float, float)]
    Point3h Gf.Vec3h(float, float, float)
    Point3hArray [Gf.Vec3h(float, float, float)]
    Quatd Gf.Quatd(float, Gf.Vec3d(float, float, float))
    QuatdArray [Gf.Quatd(float, Gf.Vec3d(float, float, float))]
    Quatf Gf.Quatf(float, Gf.Vec3f(float, float, float))
    QuatfArray [Gf.Quatf(float, Gf.Vec3f(float, float, float))]
    Quath Gf.Quath(float, Gf.Vec3h(float, float, float))
    QuathArray [Gf.Quath(float, Gf.Vec3h(float, float, float))]
    String str
    StringArray [str]
    TexCoord2d Gf.Vec2d(float, float)
    TexCoord2dArray [Gf.Vec2d(float, float)]
    TexCoord2f Gf.Vec2f(float, float)
    TexCoord2fArray [Gf.Vec2f(float, float)]
    TexCoord2h Gf.Vec2h(float, float)
    TexCoord2hArray [Gf.Vec2h(float, float)]
    TexCoord3d Gf.Vec3d(float, float, float)
    TexCoord3dArray [Gf.Vec3d(float, float, float)]
    TexCoord3f Gf.Vec3f(float, float, float)
    TexCoord3fArray [Gf.Vec3f(float, float, float)]
    TexCoord3h Gf.Vec3h(float, float, float)
    TexCoord3hArray [Gf.Vec3h(float, float, float)]
    Token str
    TokenArray string
    UChar ord(char) where char is a single letter string
    UInt int
    UInt64 long or int in Python3
    UIntArray [int]
    Vector3d Gf. Gf.Vec3d(float, float, float)
    Vector3dArray [Gf.Vec3d(float, float, float)]
    Vector3f Gf.Vec3f(float, float, float)
    Vector3fArray [Gf.Vec3f(float, float, float)]
    Vector3h Gf.Vec3h(float, float, float)
    Vector3hArray [Gf.Vec3h(float, float, float)]

2. Use Sdf.ValueTypeNames.Find(string) to get the reference from a string (valid inputs are the left column in the table above)

3.Look up the exact value for TypeName in the Sdf.ValueTypeNames module ( Sdf.ValueTypeNames.Color3h for example is valid):

from pxr import Sdf

print(dir(Sdf.ValueTypeNames))

# output is: ['Asset', 'AssetArray', 'Bool', 'BoolArray', 'Color3d',
'Color3dArray', 'Color3f', 'Color3fArray', 'Color3h', 'Color3hArray', 'Color4d',
'Color4dArray', 'Color4f', ... snipped ]
    

Coming back to Working with Attributes

You can call the prim_ref.CreateAttribute(name, type) function to create the attribute, and we can use the information above to select a valid type. It returns a reference to the attribute created, which we can set with attribute_ref.Set(value) , and again we can construct a valid value by looking up the constructor above.

from pxr import Usd, UsdGeom

stage_ref = Usd.Stage.Open('stage.usda')

# get a reference to the Xform instance, this works fine since it is also a Usd.Prim
xform_ref = UsdGeom.Xform.Define(stage, '/XformPrim')

# create an attribute reference, using an explicit reference to the type
weight_attr = xform_ref.CreateAttribute('weight', Sdf.ValueTypeNames.Float)

print(weight_attr.Get()) # prints empty string for default Float values, not 0!

print(weight_attr.Get() == None) # prints "True"
print(weight_attr.Get() == 0) # prints "False"

# to set an attribute we use the attribute_ref.Set(value) function
weight_attr.Set(42.3)

print(weight_attr.Get()) # prints "42.3"

# also, you can chain calls like so
print(xform_ref.GetPrim().GetAttribute('weight').Get()) # prints "42.3"

Hierarchy and Traversal

USD Stages are organized in a hierarchy of Prims: there is a special root prim at / and it may have N-number of direct Prim descendants, each of which can have their own tree of Prim descendants.

The path to a Prim is described by a string which starts with the root prim / and contains the Prim name separated by the path separator / until the last component is the desired Prim's name.

For example /Car/Wheel/Tire refers to the Tire prim which has parent Wheel and grandparent Car. Car's parent is the special root prim /.

In the Tutorial section on Stages there is information on how to retrieve a Prim at a given path using stage_ref.GetPrimAtPath().

Here is a refresher, we'll assume car.usda has the /Car/Wheel/Tire path:

from pxr import Usd

stage_ref = Usd.Stage.Open('car.usda')

prim_ref = stage_ref.GetPrimAtPath('/Car')

Now, if you want to get a specific child of a Prim, and you know the name, you can use prim_ref.GetChild(child_name):

from pxr import Usd

stage_ref = Usd.Stage.Open('car.usda')

prim_ref = stage_ref.GetPrimAtPath('/Car')
child_prim_ref = prim_ref.GetChild('Wheel')

# Prims can be cast as bool, so you can check if the prim exists by comparing
its bool() overload
if child_prim_ref:
    print("/Car/Wheel exists") # this will execute

print(child_prim_ref.GetPath()) # prints ""/Car/Wheel"

If you want to get all the children of a Prim, you can use prim_ref.GetChildren() which returns a list of prim references:

from pxr import Usd

stage_ref = Usd.Stage.Open('car.usda')

prim_ref = stage_ref.GetPrimAtPath('/Car')

# will return [Usd.Prim(</Car/Wheel>)]
children_refs = prim_ref.GetChildren()

If you want to traverse the entire stage, the stage_ref.Traverse() function is perfect for that, it returns an iterator:

from pxr import Usd

stage_ref = Usd.Stage.Open('car.usda')

for prim_ref in stage_ref.Traverse():
    print(prim_ref.GetPath())

There are more advanced traversal methods described in the UsdStage documentation.