All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Creating New Schema Classes with usdGenSchema

USD provides a code generator script 'usdGenSchema' for creating new schema classes. The script is driven by a USD layer (typically named schema.usda) and generates the necessary C++ classes and supporting Python code for all the schema classes defined in it.

This USD layer must meet the following prerequisites in order for generated code to compile and work with USD Core successfully.

See pxr/usd/lib/usdGeom/schema.usda for an example.

IsA Vs. API Schemas

Schema classes can be classified into the following two types:

  • API schema - API schema - An API schema provides an interface to a prim's qualities, but does not specify a typeName for the underlying prim. The prim's qualities include its inheritance structure, attributes, relationships etc. Since it cannot provide a typeName, an API schema is considered to be non-concrete. As a convention, the C++/python class name for an API schema must end with "API". In core USD, UsdModelAPI is an example of an API schema; UsdRiMaterialAPI is an example from our RenderMan schema module, which adds/extracts RenderMan-specific shading information from a generic UsdShadeMaterial-typed prim. Also by convention (with which usdGenSchema can help), the properties that "belong" to an API schema are typically namespaced with the base-name of the schema, camelCased. For example, UsdRiMaterialAPI::CreateSurfaceAttr() will create an attribute named outputs:ri:surface. API schemas are classified into the following two sub-types:
    • Non-applied API schemas - If an API schema only provides an interface to certain core bits of metadata (like UsdModelAPI, which sets model kind and UsdClipsAPI, which sets value-clips related metadata) or if there is no use of recording the application of the API schema on a prim (for the purpose of interchange), we make it a non-applied API schema. Examples of non-applied API schemas include UsdModelAPI, UsdClipsAPI, UsdShadeConnectableAPI and UsdGeomPrimvarsAPI. Typically, non-applied API schemas can apply to any prim-type (eg, UsdClipsAPI) or to a known fixed set of prim types, like in the case of UsdShadeConnectableAPI which is only applicable to Shaders and NodeGraphs.
    • Applied API Schemas - If there is a need to record and discover whether an API schema has been applied to a prim, we make it an applied API schema. An applied schema will impart its properties as additional built-in properties on the prim. A public Apply() (or private _Apply()) method is auto-generated for applied API schemas. Once an API schema has been applied to a prim, prim.HasAPI<APISchemaType>() will return true. An applied API schema must be applied to a prim via a call to the Apply() method, for the schema object to evaluate to true when converted to a bool using the explicit bool conversion operator. An applied API schema can only inherit from another applied API schema or from UsdAPISchemaBase directly. Examples of applied API schemas include UsdCollectionAPI, UsdGeomModelAPI and UsdGeomMotionAPI. Applied API schemas are further classified into the following two categories:
      • Single-Apply API Schemas - Applied API schemas that can only be applied once to a prim. Examples include UsdGeomModelAPI and UsdGeomMotionAPI.
      • Multiple-Apply API Schemas - Applied API schemas that can be applied multiple times on the same prim with different instance names. For example, UsdCollectionAPI, which must be applied once per collection owned by a prim. Properties instantiated by this API schema are prefixed with the namespace prefix of the schema followed by the instance name. There is specific metadata one authors in schema.usda to identify the prefix for multi-apply schemas, and the properties for a multi-apply schema should be listed without any prefix.
  • IsA schema - An IsA schema can impart a typeName to a prim in addition to providing an interface to a prim's qualities. Every IsA schema must derive from the core class UsdTyped, which is the base class for all typed schemas. Furthermore, an IsA schema can be concrete or non-concrete. An IsA schema will be concrete (or instantiable) if its schema declaration provides both a name for the schema (in quotes) and a typeName in the schema.usda file in which it is defined. A non-concrete (abstract) IsA schema provides only a name for the schema, and hence cannot be instantiated; non-concrete schemas exist to serve as super-classes for related sets of concrete IsA schemas. UsdGeomImageable is an example of a non-concrete IsA schema. UsdGeomScope is an example of a concrete, typed IsA schema.
Note
"Instantiable," in this context, means instantiable as a typed prim in scene description. The generated classes for all schemas, be they API, concrete-typed or non-concrete-typed are always instantiable in C++ or python as interface objects through which one can interact with UsdPrim objects.

The definitions of both IsA schemas and applied API schemas are published, at runtime, into an introspectable schema definition registry, which produces the prim definitions consulted by core USD when performing property value resolution (i.e. retrieving a property's value at a given UsdTimeCode). This allows IsA and applied API schemas to provide fallback values for their properties, that is, a value that the property will possess, even when none has been authored. Because a prim can only have one typeName, a prim can "be" (IsA) at most a single type, but can host data for any number of API schemas. This combination of IsA and applied API schemas constitutes the prim's complete type signature and is used to construct a single prim definition that provides all the built-in properties and fallback values for the prim.

The author of an API schema has to decide on the type of API schema at the time of its creation by setting token-valued customData entry 'apiSchemaType' in the schema definition. It can be set to one of 'nonApplied', 'singleApply' or 'multipleApply'. When unspecified, the fallback apiSchemaType for an API schema is 'singleApply'. An API schema can only inherit from another compatible API schema with matching customData["apiSchemaType"] or from "/APISchemaBase" directly. This is enforced by the schema code generation script 'usdGenSchema'.

API schemas and non-concrete typed schemas must not provide a typeName in their class declaration in a schema.usda file.

See Example Schema Classes for examples of each type of schema class.

Schema Code Generation

Simply run the usdGenSchema command to generate code in the current directory for the schema classes defined in the file named 'schema.usda'.

The code generator uses jinja2 templates that are installed with USD. build. The default schema class templates that ship with USD include the following files:

  • schemaClass.h, schemaClass.cpp, wrapSchemaClass.cpp: One set for each class found in schema.usda
  • tokens.h, tokens.cpp, wrapTokens.cpp: Contains static TfTokens for use with all schema in the library.
  • plugInfo.json: Every Pxr module that contains plugins has one of these. We add a declaration for every generated schema class into this file so that USD core can discover all plugin prim types cheaply and robustly.
  • api.h: Boilerplate macro definitions for exporting symbols on various platforms.

In addition to the files in schemata and tokens related files, the following files are edited by the script:

  • generatedSchema.usda: Processed form of schema definitions that will be consumed at runtime by USD core.
Note
usdGenSchema will update existing files in the current directory if it detects any differences with the code it generates. Make sure these files are editable before running usdGenSchema
usdGenSchema does not update the CMakeLists.txt and module.cpp files, even if they are editable. If you have added a new class(es), you must add them to these files yourself.

Various command-line options are available for customizing the code generation process. Run usdGenSchema --help for more details.

Namespaced Properties in Code Generation

usdGenSchema also supports the use of namespaced properties for code generation. For example, float foo:bar will generate UsdMyClass::GetFooBarAttr() and UsdTokens->fooBar (with a value of "foo:bar"). usdGenSchema will raise exceptions to avoid naming collisions in the schema API and enforces a One-to-One mapping of token identifiers to token values, as shown below.

class MyClass "MyClass" {
# Generates UsdMyClass::GetFooBarAttr() and UsdTokens->fooBar with value
# "fooBar"
float fooBar
# ERROR: Naming collision in both API and tokens. Generates
# UsdMyClass::GetFooBarAttr() and UsdTokens->fooBar with value "foo:bar"
float foo:bar
# ERROR: Naming collision in tokens. Generates UsdMyClass::GetMyTokenAttr()
# and UsdTokens->fooBar with value "foo-bar"
token myToken = "foo-bar" (allowedTokens = ["foo-bar"])
}

Global Schema Properties

Each schema.usda file can contain a GLOBAL section like the following to provide customizations that apply to all the classes in the module:

over "GLOBAL" (
customData = {
string libraryName = "pxUsdGeom"
string libraryPath = "folder/pxUsdGeom"
string libraryPrefix = "PxUsdGeom"
string tokensPrefix = "PxUsdGeom"
dictionary libraryTokens = {
dictionary libraryToken1 = {}
dictionary libraryToken2 = {
string value = "/non-identifier-tokenValue!"
string doc = """doc for libraryToken2"""
}
}
}
)
{

Here's a short description of each datum in the global customData dictionary:

  • libraryName - The name of the module into which the schema-generated files will be installed. Required!
  • libraryPath - The partial path with which to prefix '#include' statements of generated files for this module. For external (non-Pixar) plugins, we recommend setting libraryPath to ".", so that the headers inside src directory are included in the generated files. Required!
  • libraryPrefix - The prefix for all generated schema classes in the module. If not specified, falls back to ProperCase(libraryName)
  • tokensPrefix - The prefix to use for the tokens class, if it needs to be different from libraryPrefix
  • libraryTokens - a place to declare tokens meaningful to the module. These tokens will be included in the module's static tokens. If provided, the "doc" string will be included in the tokens documentation. If provided, the "value" string will be assigned as the token's value; otherwise, the token's value will be its identifier (as a TfToken).

Customizing Per-Class Properties

class PxHairman "PxHairman" (
customData = {
string className = "Hairman"
string fileName = "_hairman"
string extraIncludes = """
#include "pxr/usd/usdGeom/primvar.h"
"""
dictionary extraPlugInfo = {
string customString = "metadata"
bool customBool = true
int customInt = 0
dictionary customDict = {
string customNestedString = "nested"
}
}
dictionary schemaTokens = {
dictionary schemaToken1 = {}
dictionary schemaToken2 = {
string value = "/non-identifier-tokenValue!"
string doc = """doc for schemaToken2"""
}
}
token[] fallbackTypes = ["PrimTypeName1", "PrimTypeName2"]
}
)
{
}

Here's a short description of each datum in the per-class customData dictionary:

  • className - If the USD prim typeName for the schema must be different than the un-prefixed class name, then use className customData to provide the class name for the C++ (which will be prefixed) and python schema classes.
  • fileName - if specified, will be the base name for the .h and .cpp generated files. If not specified, base name falls back to CamelCase(className)
  • extraIncludes - if specified, will add extra include paths for files required by the "custom" section of this class only. Note that this will add includes to the generated header file for the class. If includes are only needed in the generated implementation file(.cpp), one can instead add the header includes to the custom section of the cpp file specifically.
  • extraPlugInfo - if specified, the (key, value) pairs in this dictionary will be added as additional metadata for this class in the library's plugInfo.json file. The values in this dictionary must be numeric types, strings, booleans, or dictionaries containing these types.
  • schemaTokens - a place to declare tokens meaningful to the schema. These tokens will be included in the module's static tokens. See docs above for the libraryTokens entry in the global customData dictionary for details about the contents of this dictionary.
  • apiSchemaType - must only be specified for an API schema. Defaults to the token 'singleApply', to indicate a single-apply API schema. Can be set to 'nonApplied', to create a non-applied API schema or to "multipleApply" to create a multiple-apply API schema.
  • propertyNamespacePrefix - must only be specified on multiple apply API schemas which have properties. This token, alongside the instance name, will be inserted as a prefix to all properties created by this multiple-apply API schema.
  • fallbackTypes - must only be specified for a concrete typed schema. This is a token array value used to specify the preferred fallback schema types that can be used instead when this schema isn't present. This data is used by UsdStage::WriteFallbackPrimTypes to record, on a stage, the fallback prim types metadata that USD versions which lack this schema will use to choose a suitable alternative schema type.

Customizing Per-Property

ColorFloat[] primvars:displayColor (
customData = {
string apiName = "displayColor"
}
)

Here's a short description of each datum in the per-property customData dictionary:

  • apiName - Schema properties may define an 'apiName' in customData to override the default generated accessor API. For example, the above spec produces GetDisplayColorAttr instead of GetPrimVarsDisplayColorAttr as the attribute accessor. As a special case, if 'apiName' is set to the empty string, then no accessor API will be generated. Note: The actual name of the property as defined on the prim is still primvars:displayColor.
  • apiGetImplementation - Optionally control schema gen behavior for a property's 'Get' implementation. Valid values:
    • generated - Generate default header and implementation (Fallback value if unspecified).
    • custom - Generate default header ONLY. User must supply the implementation Custom should be used sparingly, primarily as a tool for API migration Given the performance expectations of Get, it is NOT appropriate to add complicated validation logic in this method.

Example Schema Classes

#usda 1.0
(
""" This file describes an example schemata used for code generation using
usdGenSchema.
"""
subLayers = [
# This is mainly needed for definition of UsdTyped.
@usd/schema.usda@
]
)
over "GLOBAL" (
customData = {
string libraryName = "myLib"
string libraryPath = "componentName/myLib"
dictionary libraryTokens = {
dictionary sampleToken = {
string doc = "Documentation for sample token."
}
}
}
) {
}
# Example of a non-concrete IsA schema
# Note that non-concrete IsA schemas cannot specify a typeName in the class
# declaration, but they are allowed to provide fallback values for attributes.
class "MyBaseCustomPrim" (
doc = """Defines a non-instantiable (non-concrete) typed schema that derives
from MyCustomPrim. Derived schema classes can inherit from this
schema to add (for e.g.) geometric properties."""
# IsA schemas should derive from </Typed> or a Schema that derives from
# Typed.
#
# API schemas need not specify inherits. usdGenSchema sets the parent
# class for such schemas to UsdSchemaBase.
inherits = </Typed>
customData = {
string className = "MyBasePrim"
}
) {
# Some base attributes common to all derived schemas
uniform double uniformScale = 1.0 (
doc = "A double valued uniform attribute representing scale."
)
float3 rotation = (0, 0, 0) (
doc = "A varying 3D vector in floating-pt precision representing rotation."
)
double3 translation = (0, 0, 0) (
doc = "A varying double valued 3D vector representing translation."
)
}
# Example of a concrete, typed (IsA) schema
class MyCustomPrim "MyCustomPrim" (
doc = """Defines a custom typed (IsA) schema prim"""
inherits = </MyBaseCustomPrim>
customData = {
string className = "MyPrim"
customData = {
string extraIncludes = """
#include "pxr/base/gf/bbox3d.h"
#include "pxr/usd/usdGeom/primvar.h" """
}
}
) {
# Attributes with fallback values.
asset filePath = @/path/to/default/file@ (
doc = """An asset path valued attribute that points to a file on disk."""
)
uniform token axis = "X" (
allowedTokens = ["X", "Y", "Z"]
doc = """A token valued attribute representing an axis."""
)
matrix4d transform = ((1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1)) (
doc = """Double-precision transformation matrix, which should encode
the entire local transformation for a prim.""")
)
# Attributes with no fallback values.
point3f[] points (
doc = """An attribute representing a list of points in 3D space."""
)
string[] strArray (
doc = """A string array valued attribute."""
)
string str (
doc = """An int valued attribute."""
)
# Relationships
rel target (
doc = """A relationship called target that could point to another prim
or a property"""
)
}
# Example API schema that provides an interface for manipulating a specific
# set of attributes on a prim.
#
# API schemas can "declare" and provide access to properties defined by
# collections of other IsA and API schemas, gathered into one API for
# convenience. They can also (more commonly, in our use, thus far), define
# their own properties with their own fallbacks. In this capacity,
# the convention is to namespace each property with the API's name (camelCased),
# for easy identification, as well as to help prevent built-in properties from
# API schemas from unintentionally overriding built-in properties of the IsA
# schema when applied.
# For example...
class "MyParamsAPI" (
inherits = </APISchemaBase>
customData = {
apiSchemaType = "singleApply"
}
)
{
double myParams:size (
customData = {
string apiName = "size"
}
doc = "double specifying the size."
)
uniform int myParams:numSamples (
customData = {
string apiName = "numSamples"
}
doc = "Uniform int specifying the number of samples."
)
double3 myParams:offset = (0, 0, 0) (
customData = {
string apiName = "offset"
}
doc = "3D offset."
)
# By default, all properties of IsA and API schemas are considered
# "builtin", i.e. not \ref UsdProperty::IsCustom() "custom". However,
# one can force a schema property to be considered custom by explicitly
# declaring it to be so.
custom string info
}
# Example multiple-apply API that gives an interface to create instances of
# "critters" on a prim.
class "GridCrittersAPI" (
inherits = </APISchemaBase>
customData = {
token apiSchemaType = "multipleApply"
token propertyNamespacePrefix = "critter"
}
)
{
# this will be instantiated as "critter:<instance name>:xform"
matrix4d xform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) )
# this will be instantiated as "critter:<instance name>:color"
color4f color
}

See Basic Datatypes for Scene Description Provided by Sdf for the list of all data types provided by Sdf.

Adding Custom Code To Generated Schemas

Custom code written after the "// --(BEGIN CUSTOM CODE)--" delimiter in the generated schema files will be preserved between successive usdGenSchema runs. Typically, this will include additional API you may want to provide on your schema classes.

Impact on Interchange of Creating and Extending Schemas

Coming soon!