Schemas
Schemas are to USD what classes are to object orient programming. Let's explain schemas with that analogy in mind:
- Schemas are templates that define default properties and methods. You can think of each prim in your hierarchy being an instance of a class.
- Each prim must (or rather should, technically it is not enforced) have a type name set (see our prim section). The type name defines the primary class your prim is an instance of. To dynamically subclass your primary classes with additional classes, USD has the concept of API schemas. These then provide extra metadata/properties or methods that can manipulate your prim data.
The examples on this page only talk about how to apply/remove schemas and how to inspect them. In our production and Houdini section we'll look into the most used ones and run through some production examples.
Table of Contents
- API Overview In-A-Nutshell
- What should I use it for?
- Resources
- Overview
- Creating/Using schemas in your code
- Prim Definition
- Prim Type Info
- Schema Classes
TL;DR - Metadata In-A-Nutshell
- Schemas are like classes in OOP that each prim in your hierarchy then instances. They provide properties (with fallback values) and metadata as well as methods (
Get<PropertName>
/Set<PropertName>
/Utility functions) to manipulate your prim data. - There are two different base schema types (See the overview section for more info):
- Typed Schemas:
- Define prim type name (OOP: The main class of your prim), like
Cube
/Mesh
/Xform
- Provide metadata/properties and methods to edit these
- Checkable via
prim.IsA(<SchemaClassName>)
- Define prim type name (OOP: The main class of your prim), like
- API Schemas (Class Naming Convention
<SchemaClassName>API
):- Do not define prim type name (OOP: A subclass that inherits to your main class)
- Is divided in:
- Non-Applied API schemas:
- Add convenience methods to manipulate common prim data like properties and core metadata (like
kind
/clips
).
- Add convenience methods to manipulate common prim data like properties and core metadata (like
- Applied API schemas:
- Supplement typed schemas by adding additional metadata/properties and methods to edit these
- Checkable via
prim.HasAPI(<SchemaClassName>)
- Non-Applied API schemas:
- Typed Schemas:
- A prims composed schema definition can be accessed via
prim.GetPrimDefinition()
. This defines a prim's full type signature, similar to how you can inherit from multiple classes in OOPclass (<TypedSchemaClass>, <AppliedAPISchemaA>, <AppliedAPISchemaA>)
. - You can generate your own as described in our plugin schemas section.
What should I use it for?
We'll be using schema classes a lot in production, so we recommend familiarizing yourself with the below examples.
They are the main interface for your prims in the high level API that gives you getters/setters for all the standard properties that ship with Usd.
### Typed Schemas ###
# From type name
prim_type_name = prim.GetTypeName()
prim_typed_schema = Usd.SchemaRegistry.GetTypeFromName(prim_type_name).pythonClass(prim)
# From prim type info
prim_typed_schema = prim.GetPrimTypeInfo().GetSchemaType().pythonClass(prim)
### API Schemas ###
# Non-Applied API Schemas
non_applied_api_schema = Usd.ModelAPI(prim)
# Applied API Schemas
applied_api_schema = UsdGeom.MotionAPI.Apply(prim)
The schema classes then give you access to all of the schemas Get/Set methods and utility functions.
Resources
Overview
Here is a flow chart of how the schema inheritance is setup:
flowchart TD schemaBase(["Usd.SchemaBase"]) schemaTyped(["Typed Schemas (Usd.Typed) | Checkable via prim.IsA()"]) schemaTypedNonConcrete(["Non-Concrete Schemas (Abstract Classes)"]) schemaTypedConcrete(["Concrete Schemas (Define Prim Type Name)"]) schemaAPI(["API Schemas (Usd.APISchemaBase )"]) schemaAPINonApplied([Non-Applied]) schemaAPIApplied(["Applied | Checkable via prim.HasAPI()"]) schemaAPIAppliedSingle([Single Applied]) schemaAPIAppliedMulti([Multi Applied]) style schemaTypedNonConcrete fill:#57ff5f style schemaTypedConcrete fill:#63beff style schemaAPINonApplied fill:#63beff style schemaAPIAppliedSingle fill:#63beff style schemaAPIAppliedMulti fill:#63beff schemaBase --> schemaTyped schemaTyped --> schemaTypedNonConcrete schemaTypedNonConcrete --> schemaTypedConcrete schemaBase --> schemaAPI schemaAPI --> schemaAPINonApplied schemaAPI --> schemaAPIApplied schemaAPIApplied --> schemaAPIAppliedSingle schemaAPIApplied --> schemaAPIAppliedMulti
All the blue colored endpoints are the ones you'll set/apply/use via code, the green one you won't instantiate directly, but you can use it to check for inheritance.
Typed Schemas (Usd.Typed)
:- The base class for all schemas that define prim types, hence the name
Typed Schemas
- Defines properties and metadata that is attached to prims that have this type.
- We can check if it is applied to a prim via
prim.IsA(<className>)
- Accessible via
SchemaClass(prim)
e.g.UsdGeom.Imageable(prim)
(Non-concrete),UsdGeom.Xform(prim)
(concrete), to get access to the methods. To actually apply the schema, we have to set the type name as described below. Accessing a typed schema on a prim with a different type name will result in errors once you try to get/set data. To actually not guess what the typed Python class is we can runprim.GetPrimTypeInfo().GetSchemaType().pythonClass(prim)
.
- The base class for all schemas that define prim types, hence the name
Typed Schemas (Usd.Typed)
->Non-Concrete Schemas
:- The non-concrete schemas are like abstract classes in OOP. They are schemas that concrete schemas can inherit from. The purpose of these is to define common properties/metadata that a certain type of typed schemas need. (For example lights all share a non-concrete schema for the essential properties.)
- Do not define a type name (hence non-concrete).
Typed Schemas (Usd.Typed)
->Non-Concrete Schemas
->Concrete Schemas
:- Defines a type name
- In OOP terms you can think of it as the primary base class that your prim is instancing.
- Applied via
Prim.SetTypeName(<typeName>)
/PrimSpec.typeName="<typeName>"
/SchemaClass.Define(stage, Sdf.Path("/path"))
Here is an example of the inheritance graph of the UsdGeom.Imageable typed non-concrete schema:
API Schemas (Usd.APISchemaBase)
- The base class for all API Schemas, subclasses must end with
API
- In OOP terms, API schemas are classes that your primary (typed) class can inherit from to gain access to convenience methods, but also additional metadata/properties.
- The base class for all API Schemas, subclasses must end with
API Schemas (Usd.APISchemaBase)
->Non-Applied API Schemas
:- Provide only methods to manipulate existing prim data like properties and core metadata (like
kind
/clips
). Their common usage is to add convenience methods to manipulate common prim data. - They do not define any metadata/properties.
- The schema name is not written to the
apiSchemas
metadata, it therefore does not contribute to the prim definition. - Code: Applied via
SchemaClassAPI(prim)
e.g.Usd.ClipsAPI(prim)
- Provide only methods to manipulate existing prim data like properties and core metadata (like
API Schemas (Usd.APISchemaBase)
->Applied API Schemas
:- Adds additional metadata/properties to prim and provides methods to manipulate these.
- The schema name is added to the
apiSchemas
metadata, it contributes to the prim definition. - We can check if it is applied to a prim via
prim.HasAPI(<APISchemaType>)
- Applied via
SchemaClassAPI.Apply(prim)
e.g.UsdGeom.ModelAPI.Apply(prim)
/prim_spec.SetInfo("apiSchemas", Sdf.TokenListOp.Create(prependedItems=["GeomModelAPI"]))
API Schemas (Usd.APISchemaBase)
->Applied API Schemas
->Single Apply API Schemas
:- Can only be applied once per prim
API Schemas (Usd.APISchemaBase)
->Applied API Schemas
->Multi Apply API Schemas
:- Can be applied multiple times with a different instance name, properties are namespaced with the instance name.
If you want to see a list of off the schema classes that ship with USD by default check out the Usd.SchemaBase API docs page, it has a full inheritance diagram.
As covered in our prim section, Usd has a PrimDefinition/PrimTypeInfo classes we can use to inspect all properties and metadata given through applied and typed schemas on a given prim. This prim definition/type info carry the full type signature of a given prim.
Creating/Using schemas in production
Let's first look at typed schemas:
To get the class from the prim, we can run:
# From type name
prim_type_name = prim.GetTypeName()
prim_typed_schema = Usd.SchemaRegistry.GetTypeFromName(prim_type_name).pythonClass(prim)
# From prim type info
prim_typed_schema = prim.GetPrimTypeInfo().GetSchemaType().pythonClass(prim)
This way we don't have to find and import the right class ourselves.
To summarize the below code:
### Typed Schemas ###
# From type name
prim_type_name = prim.GetTypeName()
prim_typed_schema = Usd.SchemaRegistry.GetTypeFromName(prim_type_name).pythonClass(prim)
# From prim type info
prim_typed_schema = prim.GetPrimTypeInfo().GetSchemaType().pythonClass(prim)
### API Schemas ###
# Non-Applied API Schemas
non_applied_api_schema = Usd.ModelAPI(prim)
# Applied API Schemas
applied_api_schema = UsdGeom.MotionAPI.Apply(prim)
###### Typed Schemas ######
### High Level ###
# Has: 'IsA',
# Get: 'GetTypeName'
# Set: 'SetTypeName'
# Clear: 'ClearTypeName'
from pxr import Sdf, Usd, UsdGeom
stage = Usd.Stage.CreateInMemory()
# Define prim via stage
prim_path = Sdf.Path("/bicycleA")
prim = stage.DefinePrim(prim_path, "Cube")
# Define prim via typed schema
prim_path = Sdf.Path("/bicycleB")
prim_typed_schema = UsdGeom.Cube.Define(stage, prim_path)
# Returns the schema class object, so we have to get the prim
prim = prim_typed_schema.GetPrim()
# Since the "Cube" schema is a subclass of the
# non.concrete typed UsdGeom.Boundable schema, we can check:
print(prim.IsA(UsdGeom.Cube)) # Returns: True
print(prim.IsA(UsdGeom.Boundable)) # Returns: True
# To remove the type, we can call:
# prim.ClearTypeName()
# To access the schema class methods, we give our prim to the
# class constructor:
prim_typed_schema = UsdGeom.Cube(prim)
# The typed Cube schema for example has a Get/Set method for the schema's size attribute.
prim_typed_schema.GetSizeAttr().Set(5)
# Or we let Usd gives us the Python class
prim_typed_schema = prim.GetPrimTypeInfo().GetSchemaType().pythonClass(prim)
prim_typed_schema.GetSizeAttr().Set(10)
# Or we get it from the type name
prim_typed_schema = Usd.SchemaRegistry.GetTypeFromName(prim.GetTypeName()).pythonClass(prim)
### Low Level ###
# To set typed schemas via the low level API, we just
# need to set the PrimSpec.typeName = "<SchemaName>"
from pxr import Sdf
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/bicycle")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.typeName = "Cube"
The 'IsA' check is a very valuable check to see if something is an instance of a (base) class. It is similar to Python's isinstance method.
And the API schemas:
###### API Schemas ######
### High Level ###
# Has: 'HasAPI', 'CanApplyAPI'
# Get: 'GetAppliedSchemas'
# Set: 'AddAppliedSchema', 'ApplyAPI'
# Clear: 'RemoveAppliedSchema', 'RemoveAPI'
from pxr import Sdf, Usd, UsdGeom
stage = Usd.Stage.CreateInMemory()
### Applied Schemas ###
# Define prim via stage
prim_path = Sdf.Path("/bicycleA")
prim = stage.DefinePrim(prim_path, "Cube")
# Check if it can be applied
print(UsdGeom.MotionAPI.CanApply(prim)) # Returns True
# Apply API schema (in active layer),
prim.ApplyAPI("GeomModelAPI") # Returns: True, older USD versions: prim.ApplyAPI("UsdGeomModelAPI")
# Add applied schema
# This does not check if the schema actually exists,
# you have to use this for codeless schemas.
prim.AddAppliedSchema("SkelBindingAPI") # Returns: True #
# Apply and get the schema class (preferred usage)
applied_api_schema = UsdGeom.MotionAPI.Apply(prim)
# Remove applied schema (in active layer)
# prim.RemoveAppliedSchema("SkelBindingAPI")
# prim.RemoveAPI("GeomModelAPI")
# For multi-apply schemas, we can feed in our custom name,
# for example for collections it drives the collection name.
prim.ApplyAPI("UsdCollectionAPI", "myCoolCollectionName")
applied_multi_api_schema = Usd.CollectionAPI.Apply(prim, "myCoolCollectionName")
### Non-Applied Schemas ###
# Non-Applied schemas do not have an `Apply` method
# (who would have guessed that?)
non_applied_api_schema = Usd.ModelAPI(prim)
### Low Level ###
# To set applied API schemas via the low level API, we just
# need to set the `apiSchemas` key to a Token Listeditable Op.
from pxr import Sdf
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/bicycle")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
schemas = Sdf.TokenListOp.Create(
prependedItems=["SkelBindingAPI", "UsdGeomModelAPI"]
)
prim_spec.SetInfo("apiSchemas", schemas)
# We don't have nice access the the schema class as in the high level API
Prim Definition
With the prim definition we can inspect what the schemas provide. Basically you are inspecting the class (as to the prim being the instance, if we compare it to OOP paradigms). In production, you won't be using this a lot, it is good to be aware of it though. If you change things here, you are actually on run-time modifying the base class, which might cause some weird issues.
from pxr import Sdf, Tf, Usd, UsdGeom
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.ApplyAPI("GeomModelAPI") # Older USD versions: prim.ApplyAPI("UsdGeomModelAPI")
prim_def = prim.GetPrimDefinition()
print(prim_def.GetAppliedAPISchemas()) # Returns: ['GeomModelAPI']
print(prim_def.GetPropertyNames())
# Returns: All properties that come from the type name schema and applied schemas
"""
['model:drawModeColor', 'model:cardTextureZPos', 'model:drawMode', 'model:cardTextureZNeg',
'model:cardTextureYPos', 'model:cardTextureYNeg', 'model:cardTextureXPos', 'model:cardTextur
eXNeg', 'model:cardGeometry', 'model:applyDrawMode', 'proxyPrim', 'visibility', 'xformOpOrde
r', 'purpose']
"""
# You can also bake down the prim definition, this won't flatten custom properties though.
dst_prim = stage.DefinePrim("/flattenedExample")
dst_prim = prim_def.FlattenTo(dst_prim)
# This will also flatten all metadata (docs etc.), this should only be used, if you need to export
# a custom schema to an external vendor. (Not sure if this the "official" way to do it, I'm sure
# there are better ones.)
Prim Type Info
The prim type info holds the composed type info of a prim. You can think of it as as the class that answers Python type()
like queries for Usd. It caches the results of type name and applied API schema names, so that prim.IsA(<typeName>)
checks can be used to see if the prim matches a given type.
from pxr import Sdf, Tf, Usd, UsdGeom
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.ApplyAPI("GeomModelAPI")
print(prim.IsA(UsdGeom.Xform)) # Returns: True
print(prim.IsA(Tf.Type.FindByName('UsdGeomXform'))) # Returns: True
prim_type_info = prim.GetPrimTypeInfo()
print(prim_type_info.GetAppliedAPISchemas()) # Returns: ['GeomModelAPI']
print(prim_type_info.GetSchemaType()) # Returns: Tf.Type.FindByName('UsdGeomXform')
print(prim_type_info.GetSchemaTypeName()) # Returns: Xform
Schema Classes
We can lookup all registered schemas via the plugin registry as well as find out what plugin provided a schema.
Before we do that let's clarify some terminology:
Schema Type Name
: The name of the schema class, e.g.Cube
,Imageable
,SkelBindingAPI
Tf.Type.typeName
registry name: The full registered type nameUsdGeomCube
,UsdGeomImageable
,UsdSkelBindingAPI
We can map from schema type name
to Tf.Type.typeName
via:
registry = Usd.SchemaRegistry()
registry.GetTypeFromName("Cube").typeName # Returns: "UsdGeomCube"
We can map from Tf.Type.typeName
to schema type name
via:
registry = Usd.SchemaRegistry()
registry.GetSchemaTypeName("UsdGeomCube") # Returns: "Cube"
Schema Registry
Let's list all the schemas:
from pxr import Plug, Tf, Usd
registry = Plug.Registry()
print(">>>>>", "Typed Schemas")
for type_name in registry.GetAllDerivedTypes(Usd.Typed):
print(type_name)
print(">>>>>", "API Schemas")
for type_name in registry.GetAllDerivedTypes(Usd.APISchemaBase):
print(type_name)
# For example to lookup where the "Cube" type is registered from,
# we can run:
print(">>>>>", "Cube Schema Plugin Source")
plugin = registry.GetPluginForType(Tf.Type.FindByName("UsdGeomCube"))
print(plugin.name)
print(plugin.path)
print(plugin.resourcePath)
print(plugin.metadata)
This allows us to also look up the Tf.Type from schema (type) names,
which we can then use in IsA()
checks.
from pxr import Plug, Sdf, Tf, Usd
registry = Usd.SchemaRegistry()
## Get Tf.Type registry entry (which allows us to get the Python class)
## The result can also be used to run IsA checks for typed schemas.
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycleA")
prim = stage.DefinePrim(prim_path, "Cube")
print(prim.IsA(registry.GetTypeFromName("UsdGeomImageable"))) # Returns: True
print(prim.IsA(registry.GetTypeFromName("UsdGeomImageable").pythonClass)) # Returns: True
# GetTypeFromName allows prim type names and the Tf.Type.typeName.
print(registry.GetTypeFromName("UsdGeomCube")) # Returns: Tf.Type("UsdGeomCube")
print(registry.GetTypeFromName("Cube")) # Returns: Tf.Type("UsdGeomCube")
# For typed schemas we can also use:
print(registry.GetTypeFromSchemaTypeName("Imageable")) # Returns: Tf.Type('UsdGeomImageable') -> Tf.Type.typeName gives us 'UsdGeomImageable'
print(registry.GetTypeFromSchemaTypeName("Cube")) # Returns: Tf.Type("UsdGeomCube") -> Tf.Type.typeName gives us 'UsdGeomCube'
print(registry.GetSchemaTypeName("UsdGeomImageable")) # Returns: "Imageable"
print(registry.GetSchemaTypeName("UsdGeomCube")) # Returns: "Cube"
# For concrete typed schemas:
print(registry.GetConcreteSchemaTypeName("UsdGeomCube")) # Returns: "Cube"
print(registry.GetConcreteTypeFromSchemaTypeName("Cube")) # Returns: Tf.Type("UsdGeomCube")
# For API schemas:
print(registry.GetAPISchemaTypeName("UsdSkelBindingAPI")) # Returns: "SkelBindingAPI"
print(registry.GetAPITypeFromSchemaTypeName("SkelBindingAPI")) # Returns: Tf.Type("UsdSkelBindingAPI")
A practical use case of looking thru the registry, is that we can grab the prim definitions. We can use these to inspect what properties a schema creates. We can use this to for example builds UIs that list all the schema attributes.
from pxr import Usd
registry = Usd.SchemaRegistry()
## Useful inspection lookups ##
# Find API schemas. This uses the `Schema Type Name` syntax:
cube_def = registry.FindConcretePrimDefinition("Cube")
print(cube_def.GetPropertyNames())
# Returns:
"""
['doubleSided', 'extent', 'orientation', 'primvars:displayColor',
'primvars:displayOpacity', 'purpose', 'size', 'visibility',
'xformOpOrder', 'proxyPrim']
"""
skel_bind_def = registry.FindAppliedAPIPrimDefinition("SkelBindingAPI")
print(skel_bind_def.GetPropertyNames())
# Returns:
"""
['primvars:skel:geomBindTransform', 'primvars:skel:jointIndices',
'primvars:skel:jointWeights', 'skel:blendShapes', 'skel:joints',
'skel:animationSource', 'skel:blendShapeTargets', 'skel:skeleton']
"""
Schema Kind
We can also inspect the schema kind. The kind defines (if we look at our inheritance tree in overview) what kind of schema it is.
The kind can be one of:
- Usd.SchemaKind.AbstractBase
- Usd.SchemaKind.AbstractTyped
- Usd.SchemaKind.ConcreteTyped
- Usd.SchemaKind.NonAppliedAPI
- Usd.SchemaKind.SingleApplyAPI
- Usd.SchemaKind.MultipleApplyAPI
from pxr import Plug, Sdf, Tf, Usd
### Check schema types ###
registry = Usd.SchemaRegistry()
## Typed Schemas ##
print(registry.IsTyped(UsdGeom.Cube)) # Returns: True
print(registry.IsTyped(UsdGeom.Imageable)) # Returns: True
print(registry.IsAbstract(UsdGeom.Imageable)) # Returns: True
print(registry.IsAbstract(UsdGeom.Cube)) # Returns: False
print(registry.IsConcrete(UsdGeom.Imageable)) # Returns: False
print(registry.IsConcrete(UsdGeom.Cube)) # Returns: True
# Also works with type name strings
print(registry.IsTyped("UsdGeomImageable")) # Returns: True
print(registry.IsTyped("UsdGeomCube")) # Returns: True
## API Schemas ##
print(registry.IsAppliedAPISchema("SkelBindingAPI")) # Returns: True
print(registry.IsMultipleApplyAPISchema("CollectionAPI")) # Returns: True
## We can also ask by schema type name
print(registry.GetSchemaKind("Cube")) # Returns: pxr.Usd.SchemaKind.ConcreteTyped
print(registry.GetSchemaKind("Imageable")) # Returns: pxr.Usd.SchemaKind.AbstractTyped