Authoring USD Data#

The OpenUSD Exchange SDK helps developers implement their own USD I/O solutions that produce consistent and correct USD assets across diverse 3D ecosystems.

It provides higher-level convenience functions on top of lower-level USD concepts, so developers can quickly adopt OpenUSD best practices when mapping their native data sources to OpenUSD-legible data models.

The goal is not to abstract the USD authoring process, but instead to simplify it, by providing convenience functions that cover common use cases and avoid common stumbling blocks.

Each of the subsections below describes a common challenge with OpenUSD and links to the relevant functions and classes in OpenUSD Exchange that aim simplify these problems.

Layers and Stages#

Whenever you are authoring OpenUSD data, ultimately, your data will need to be stored in a SdfLayer, whether it is in-memory, in a local or shared filesystem, or in cloud storage.

While you can author data directly via OpenUSD’s Sdf library, it is often more intuitive and convenient to author the layer via a UsdStage instead, so you can make use of higher level schemas.

In either approach, your Layers should contain particular metadata to ensure they will load correctly across diverse 3D ecosystems. It is very common to forget (or misconfigure) this metadata. We provide SdfLayer authoring and UsdStage Configuration functions to assist & prevent common mistakes.

When authoring UsdPrims to a Stage, you will need to specify an SdfPath that identifies a unique location for the Prim. The nature of OpenUSD’s composition algorithm (know as “LIVERPS”) makes it fairly complex to determine whether your chosen location is valid for authoring. We provide UsdStage Prim Hierarchy functions to assist.

Binary vs Text Layers#

SdfLayers can be written in several formats, the most common of which is .usd. However, any .usd file could be either USDA encoded (human-readable text) or USDC encoded (a binary Crate encoding). Both encodings are also available as their own dedicated file extensions (.usda and .usdc), which help clarify the intent of content & prevent encoding mistakes.

It is important to consider your content when choosing the encoding for your SdfLayer. A good default is to always prefer USDC encoding, but for lightweight “interface” layers or quick debugging layers it may be preferable to choose USDA encoding. Further guidance can be found here.

USDC Crate Version#

When using USDC encoding, any new layers will be saved with the default Crate Version of the running process. As the default Crate Version differs across USD runtimes, this has implications on which USD Ecosystem products will be able to load the layer. UsdCrateInfo is a useful class to help determine your software’s USDC capabilities.

  • Use UsdCrateInfo::GetSoftwareVersion to determine the newest possible Crate Version that your runtime could read.

  • Use UsdCrateInfo::GetFileVersion to determine the Crate Version with which a given SdfLayer was serialized.

  • To determine your current default Crate Version, serialize a new layer and check UsdCrateInfo::GetFileVersion.

    • Note this will not necessarily match GetSoftwareVersion; it is common for a runtime to serialize an older Crate Version than it can read, to maximize portability to other runtimes.

  • If you need to target older runtimes than your default allows, be sure to set the TfEnvSetting USD_WRITE_NEW_USDC_FILES_AS_VERSION before starting the process.

Valid and Unique Names#

OpenUsd has strict requirements on what names are valid for a UsdObject, which includes both UsdPrim and UsdProperty objects.

An identifier is valid if it follows the C/Python identifier convention; that is, it must be at least one character long, must start with a letter or underscore, and must contain only letters, underscores, and numerals.

Additionally the names of sibling Objects must be unique so that the SdfPath that identifies them is unique within the UsdStage.

Ascii and UTF-8 Support#

In some current OpenUSD runtimes, valid characters within identifiers are restricted to the minimal ascii characters [A-Za-z0-9_]. The name of a UsdProperty can contain : delimiters for namespaces, however the values within each namespace must be a valid identifier.

Note

In OpenUSD v24.03 and beyond, XID identifiers are natively supported, but some reserved characters remain illegal (e.g. /, @).

For many data sources, native items will not conform to these requirements and the names will need to be made valid in order to be used in USD.

The Prim and Property Names functions can be used to produce valid UsdObject names for any OpenUSD runtime that we support.

Defining Prims#

OpenUsd provides Schema classes for authoring typed Prims, however in order to author a complete and correct Prim it is often necessary to call multiple functions. It is common for some of these functions to be over looked, or have mismatched data supplied.

The OpenUSD Exchange SDK provides “define” functions to address this problem. The “define” functions are the primary entry point for authoring 3D data to USD.

The role of a “define” function is to:

  • Ensure that a complete Prim definition is authored via a single function call

  • Perform validation on the supplied data before authoring the Prim.

    • If any of the supplied data is invalid, then the Prim will not be authored. This up front validation avoids partial authoring of Prims.

  • Ensure all opinions that contribute to the Prim’s definition will be explicitly authored in a single Layer.

To learn about each of the “define” functions in more detail, see the specific documentation:

Defining Primvars#

All UsdGeomPointBased prims can optionally have geometric surface varying variables called UsdGeomPrimvars (primitive variables or simply “primvars”) which interpolate across a primitive’s topology, and can override shader inputs. In addition, any UsdPrim can have constant primvars, which are inherited down prim hierarchy to provide a convenient set-once-affect-many workflow within a hierarchy.

UsdGeomPrimvars are often used when authoring UsdGeomPointBased prims (e.g meshes, curves, and point clouds) to describe surface varying properties such as normals, widths, displayColor, and displayOpacity. They can also be used to describe completely bespoke user properties that can affect how a prim is rendered, or to drive a surface deformation.

However, UsdGeomPrimvar data can be quite intricate to use, especially with respect to indexed vs non-indexed primvars, element size, the complexities of VtArray detach (copy-on-write) semantics, and the ambiguity of “native” attributes vs primvar attributes (e.g. mesh normals).

We provide a templated PrimvarData class to encapsulate all this data as a single object without risk of detaching (copying) arrays, and to provide simpler entry points to avoid common mistakes with respect to UsdGeomPrimvar data handling.

All of our USD authoring “define” functions for UsdGeomPointBased prims accept optional PrimvarData to define e.g normals, display colors, etc.

The PrimvarData class also supports reading from (and authoring to) any existing UsdGeomPrimvar, which may have been created via OpenUSD’s UsdGeomPrimvarsAPI.

Working with 3D Transformation#

The UsdGeomXformable schema supports a rich set of transform operations from which a resulting matrix can be computed.

The flexibility of this system adds complexity to the code required for authoring and retrieving transform information. The usdex_core library provides the 3D Transformation functions to help with this.

Physics#

UsdPhysics defines the physics-related prim and property schemas that together form a physics simulation representation. It is primarily designed around rigid body simulators, which take as input a list of rigid bodies and a list of constraints.

Many concepts in UsdPhysics are fairly straightforward and don’t merit higher level authoring functions. For example:

  • To make any UsdGeomXformable prim a rigid body use UsdPhysicsRigidBodyAPI::Apply(prim)

  • To make any UsdGeomGPrim a collision object use UsdPhysicsCollisionAPI::Apply(prim)

Tip

Use the Asset Validator to ensure the UsdPhysics schemas have been used correctly.

However, some other UsdPhysics concepts are more intricate to author correctly, especially given often divergent approaches in the source data specifications across maximal coordinate (free-body) and reduced coordinate solvers.

Properly defining PhysicsJoints relative to both bodies can be arduous. The usdex_core library provides several Physics Joints functions to simplify the authoring process and ensure PhysicsJoints are aligned to both bodies, regardless of the source data specification.

Another complex aspect of UsdPhysics is specifying friction and other material properties via PhysicsMaterialAPI, which needs to be bound to the collision geometry similarly to how visual materials are bound to the render geometry. The usdex_core library provides Physics Material functions to define, apply, and bind physics material properties like friction.

Asset Structure#

An asset is a named, versioned, and structured container of one or more resources which may include composable OpenUSD layers, textures, volumetric data, and more. There are many approaches to structuring assets, and no one structure is ideal for all use cases.

NVIDIA’s Principles of Scalable Asset Structure article proposes four key principles that should always be considered: legibility, modularity, performance, and navigability.

It goes further, to discuss pros and cons of various production proven asset structures, and makes some explicit recommendations in the form of example assets towards the end of the article.

The usdex_core library provides an Asset Structure module which aims to codify authoring of asset structures that have been proven scalable and have broad import compatibility across a wide range of OpenUSD enabled applications, while guiding and simplifying the development process for new OpenUSD Exporters.

In particular, we provide functions for:

  • Static Tokens (strings) which can be used for Layer or Scope names, to ensure consistency across assets

  • Defining relative References/Payloads whenever possible, and absolute References/Payloads only when necessary

  • Creating the Atomic Component structure proposed in the article

    • Creating and organizing Content Layers and Library Layers in a consistent manner

    • Configuring an Asset Interface Layer, which payload’s the content for deferred loading, while still exposing key metadata to the consumer

Diagnostic Logs#

OpenUSD’s Tf library provides various diagnostic logging facilities which are useful for communicating errors, warnings, and status information to end users or system logs.

However, the default diagnostic output is somewhat engineer-centric. Fortunately, it provides the ability to override the default message handler, with one or more custom diagnostic delegates.

We provide one such diagnostic delegate with some more controllable options (like level filtering), which you are welcome to use directly, or to use as inspiration for your own diagnostic delegate implementation.

Debug logs are emitted via OpenUSD’s TfDebug mechanism, which is separate from the other diagnostics. See our Debugging guide for more information.

Runtime Settings#

Some OpenUSD Exchange behaviors (such as name transcoding) are controllable via global static runtime settings, using OpenUSD’s TfEnvSetting mechanism, which relies on setting Environment Variables before the USD libraries are loaded into an application.

Python Interoperability#

Many projects use pybind11 for python bindings, but OpenUSD 24.08 and older uses boost::python, while OpenUSD 24.11 and newer uses a fork of boost::python called pxr_python.

We often need to pass the python objects in and out of c++ between a mix of bound functions. OpenUSD Exchange provides usdex/pybind C++ headers to enable pybind11 to consume & to produce OpenUSD bound objects.