|
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.
Schema classes can be classified into the following two types:
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.
In some cases an IsA schema may want to have certain API schemas always applied to prims of its schema type. To avoid having to manually apply these API schemas to all instances of these prim types we provide a few ways to specify built-in applied API schemas for IsA schema types. When a prim of an IsA schema type with built-in applied API schemas is created, all properties from the API schemas will also be built-in properties of the prim. The list of built-in API schemas can be queried from the prim type's prim definition and the prim will also return true from UsdPrim::HasAPI for all of its built-in API schemas.
Furthermore, for some applied API schema types, we may want the application of the API schema to additionally apply one or more other API schemas. We also provide, through the same mechanisms as we have for IsA schemas, the ability to specify built-in applied API schemas for both single-apply and multiple-apply API schema types. When an API schema with built-in API schemas is applied to a prim (or is included as a built-in for another type), all of its built-in API schemas are also applied. These built-ins nest, so an API schema can include another built-in API schema which itself includes yet another built-in API schema and all these will be applied when the top level API schema is applied.
The built-ins of both IsA and single-apply API schemas may only contain the names of single-apply API schemas and named instances of multiple-apply API schemas (such as "ExampleSingleApplyAPI" and "MultiApplyAPI:foo").
For multiple-apply schemas, since they must always be applied to a prim using an instance name, their built-in schemas must be able to be applied to a prim using the same instance name (or a suffixed version of it) as the schema they're built in to. Thus, the schema names listed as built-ins for multiple-apply API schemas must only refer to other multiple-apply API schemas and can be specified in two ways:
The first and most straightforward way an IsA or applied API schema author can specify which applied API schemas it wants built-in is by prepending them to the apiSchemas field in their schema definition like so:
The listed API schemas will be built-in to the declared schema type. For IsA schemas, derived types of the schema will inherit its built-in API schemas (applied API schemas cannot inherit from each other). The apiSchemas field must always be specified with prepend as we only allow a schema to add new built-in API schemas that are stronger than any that may be inherited from its parent schema. Any schema can specify overrides to properties that would come from one of its built-in API schemas by declaring the property itself. This should be done carefully to avoid breaking conformance with the built-in schema itself (like changing the property's type) and is typically used for changing the default value for the property.
In addition to schemas being able to declare built-in applied API schemas, an applied API schema can specify that it should be automatically applied to any number of IsA or single-apply API schemas. The primary use case for this is when a schema author wants to extend an existing schema with properties from their own applied API schema without altering the existing schema itself. Specifying the API schema to auto apply can accomplish this goal without having to manually apply the API to every prim instance of a schema type (or to every prim instance with a specific API schema applied). A concrete example would be a renderer providing an API schema for its own specific render properties that can then be automatically applied to UsdLux light typed prims when the renderer and its API schema are available.
An author of an API schema can specify the IsA and/or single-apply API schemas to which it wants to be automatically applied through the apiSchemaAutoApplyTo field in the API schema definition's customData like so:
The specified schema names in the apiSchemaAutoApplyTo field can refer to another single-apply API schema and to both abstract and concrete IsA schema types. For the IsA schema types, the applied schema will be added to the built-in schemas for the listed types as well as for any derived schemas of the listed types. Note that this customData field is only supported for single apply API schemas as multiple apply API schemas cannot be applied without an instance name.
Lastly, it is also possible for plugins to specify additional built-in API schemas for other schema types outside of schema generation. This is useful if a client desires schema types to have built-in API schemas but doesn't want to force this upon all clients by changing the generated schemas themselves. An example use case for this would be a client pipeline that wants an available UI related API schema applied to all shader prim types as this is not something all users of these schemas may necessarily want.
A plugin author can specify their additional built-in API schemas in their plugin's plugInfo.json file using the AutoApplyAPISchemas metadata like so:
The metadata format is a dictionary of API schema names to a list of the schemas the plugin wants them to be applied to as a built-in API. Like with the schema generated auto apply schemas, the API schemas will also be applied to any derived schema types of any listed IsA schemas. Multiple apply schemas (in addition to single apply) can also be auto applied using this plugin metadata as long as the fully qualified instance name for the mulitple apply schema is provided.
Given that we encourage (or even require in the case of multiple-apply schemas) the use of namespace prefixes for API schema property names, we expect most properties defined in API schemas will be unique to the schema. However, there will always be the possibility that properties with the same name may be defined in multiple API schemas applied to the same prim. To account for this, there is a strength ordering for applied API schemas that determines which property definition "wins" when more than one API schema defines a property of the same name.
In defining schema strength ordering, it is helpful to talk about a schema order where the first schema is the strongest and the last schema is the weakest. The strength order of schemas is as follows:
The strongest opinion for all aspects of a built-in property in the IsA schema's prim definition. I.e., the IsA schema comes first in the schema order.
The strength order of applied API schemas is determined by adding them to the schema order, after the IsA schema, as follows:
apiSchemas
metadata are added to the schema order, immediately after the schema that introduces them, in the order they appear in the composed apiSchemas
field.This applied API strength order is invoked recursively, in a depth first expansion, because all schema types can introduce built-in schemas and can cause additional auto applied schemas to be applied.
For properties that are defined by more than one of the included schemas, fields for which the strongest property definition does not express an opinion, may be composed from weaker API schema definitions (i.e., the weaker definition may act as an API schema underride) if the following conditions are met:
Sometimes when an IsA or an API schema includes another API schema as a built-in, it may also want to alter one or more of the built-in schema's properties to give the property a more reasonable fallback value or change some other metadata on the property like its documentation or its allowedTokens. But we don't want the schema to have to fully redefine the property when it only wants to sparsely override the metadata it cares about. In order to enable this behavior we allow a schema to explicitly tag a property as an API schema override.
A schema property that is declared as an API schema override does not define the property in that schema. Instead it behaves as an over for a property with the same name that may be defined by any of its included built-in API schemas. This is different than the standard behavior when included API schemas define the same property (without specifying an override) as in that case, the strongest schema's property wins and completely stomps over the same property from any weaker schema (as specified in Property conflicts, composition, and API schema strength ordering).
API schema overrides can be declared for both IsA and applied API schemas in the source schema files through adding
to the customData of the property. When the schema registry is populated, any properties declared as API schema overrides will be composed over the defined property from the schema's built-in API schemas if a property with that name is indeed defined in one of the built-in schemas. If no property with the name exists in the included API schemas, the API schema override property is ignored.
An example of a common use case for this are schemas that include instances of CollectionAPI and want to only change the fallback for includeRoot or expansionRule, e.g.:
Because we treat auto-apply API schemas as an extension of the built-in API schemas included in a schema, API schema override properties can also be used to sparsely override properties defined in auto-apply API schemas. Since auto-apply schemas will typically be defined in plugins, API schema override properties are a convenient way to allow a schema to override a property that it expects to come from an auto-apply API schema without defining the property itself when the auto-apply API schema is not present (like when the auto-apply API schema's plugin isn't included).
There are some limitations to how API schema override properties can be used:
Note that API schema property overrides are specific to overriding properties from built-in API schemas and do not apply to overriding properties via typed schema inheritance. This is because schema properties always sparsely compose via class inheritance during schema generation so no override specification is necessary.
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:
In addition to the files in schemata and tokens related files, the following files are edited by the script:
Various command-line options are available for customizing the code generation process. Run usdGenSchema --help
for more details.
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. Note that irrespective of useLiteralIdentifier value, property names with ':' are always camelCased for the defined convention.
Each schema.usda file can contain a GLOBAL section like the following to provide customizations that apply to all the classes in the module:
Here's a short description of each datum in the global customData dictionary:
By default usdGenSchema
generates C++ and Python code, providing appropriate APIs. Clients also have an option of not generating any code by setting the skipCodeGeneration metadata to True for a given schema, hence generating only generatedSchema.usda and plugInfo.json which are the only essential products for runtime schema registration.
Since codeless schemas do not provide any code, clients do not need to recompile USD to use or update these schemas. This "dynamic" nature of codeless schemas is the primary motivation behind using codeless schemas. Also, for the same reasons, clients will have to use basic USD level APIs to query prims and attributes associated with the codeless schemas. This also means codeless schemas concept can not be used for any schema which requires custom code.
The usdRiPxr schema domain that ships with USD is an example of a codeless schema domain. These are usd schemas generated using renderman shader definitions (args files) using the Sdr library. usdgenschemafromsdr
is the utility which generates schema.usda, generatedSchema.usda and plugInfo.json for the usdRiPxr schema domain.
Here's a short description of each datum in the per-class customData dictionary:
Here's a short description of each datum in the per-property customData dictionary:
See Basic Datatypes for Scene Description Provided by Sdf for the list of all data types provided by Sdf.
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.
Coming soon!