USD
Opening USD Stages
Working with USD Stages is straightforward, as most times everything is one function call away.
To load a USD file as a USD Stage you use Usd.Stage.Open(path)
:
Note that this does not load the scene into Kit! (see omni.usd
docs for this)
from pxr import Usd
from omni.kit.usd_docs import single_prim_usda
stage = Usd.Stage.Open(single_prim_usda)
print (f"opened stage {stage}")
To create a new Stage use Usd.Stage.CreateNew(path)
;
from pxr import Usd
stage = Usd.Stage.CreateNew('a_new_stage.usd')
print (f"created stage {stage}")
To save a loaded Stage use Usd.Stage.Save(path)
from pxr import Usd
from omni.kit.usd_docs import single_prim_usda
stage = Usd.Stage.Open(single_prim_usda)
# do something to the stage
stage.Save()
print (f"saved stage {stage}")
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
from omni.kit.usd_docs import single_prim_usda
stage = Usd.Stage.Open(single_prim_usda)
# do something to the stage
stage.Export('a_usdc_file.usdc')
print (f"exported stage {stage}")
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
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_usda)
prim = stage_ref.GetPrimAtPath('/hello')
print(prim.GetName()) # prints "hello"
print(prim.GetPrimPath()) # prints "/hello"
To define a new prim use stage_ref.DefinePrim(path)
:
from pxr import Usd
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_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
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_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
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_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
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_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:
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]|
| DoubleArray | [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.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)]|
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, Sdf
from omni.kit.usd_docs import single_prim_usda
stage_ref = Usd.Stage.Open(single_prim_usda)
# get a reference to the Xform instance, this works fine since it is also a Usd.Prim
xform_ref = UsdGeom.Xform.Define(stage_ref, '/XformPrim')
# create an attribute reference, using an explicit reference to the type
weight_attr = xform_ref.GetPrim().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()
.
from pxr import Usd
from omni.kit.usd_docs import car_usda
stage_ref = Usd.Stage.Open(car_usda)
prim_ref = stage_ref.GetPrimAtPath('/Car')
print (f"car prim {prim_ref}")
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
from omni.kit.usd_docs import car_usda
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
from omni.kit.usd_docs import car_usda
stage_ref = Usd.Stage.Open(car_usda)
prim_ref = stage_ref.GetPrimAtPath('/Car')
print (f"prim_ref {prim_ref}")
# will return [Usd.Prim(</Car/Wheel>)]
children_refs = prim_ref.GetChildren()
print (f"children_refs {children_refs}")
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
from omni.kit.usd_docs import car_usda
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.