Book of USD

Universal Scene Description (USD) is a novel way of defining and working with 3D-Scene data, developed by Pixar Animation Studios.

This book aims to be a companion document for the official documentation

License (MIT)

MIT License

Copyright (c) 2022 Remedy Entertainment

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Most images in this book are using the Animal Logic ALab USD Scene.

Animal Logic ALab License (ASWF Digital Assets License v1.1)

ASWF Digital Assets License v1.1

License for Animal Logic ALab (the “Asset Name”).

Animal Logic ALab Copyright 2022 Animal Logic Pty Limited. All rights reserved.

Redistribution and use of these digital assets, with or without modification, solely for education, training, research, software and hardware development, performance benchmarking (including publication of benchmark results and permitting reproducibility of the benchmark results by third parties), or software and hardware product demonstrations, are permitted provided that the following conditions are met:

  1. Redistributions of these digital assets or any part of them must include the above copyright notice, this list of conditions and the disclaimer below, and if applicable, a description of how the redistributed versions of the digital assets differ from the originals.
  2. Publications showing images derived from these digital assets must include the above copyright notice.
  3. The names of copyright holder or the names of its contributors may NOT be used to promote or to imply endorsement, sponsorship, or affiliation with products developed or tested utilizing these digital assets or benchmarking results obtained from these digital assets, without prior written permission from copyright holder.
  4. The assets and their output may only be referred to as the Asset Name listed above, and your use of the Asset Name shall be solely to identify the digital assets. Other than as expressly permitted by this License, you may NOT use any trade names, trademarks, service marks, or product names of the copyright holder for any purpose.

DISCLAIMER: THESE DIGITAL ASSETS ARE PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE DIGITAL ASSETS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Contributing

This is an open project and any contributions are welcome and appreciated! See the GitHub Repository for more information

Motivation

Universal Scene Description (USD) is already making big waves in the VFX/Film and Games industries, with large players such as NVidia, Apple and Autodesk jumping on the technology. But as a newcomer; it can be an incredibly daunting and frustrating journey to venture into.

This book’s driving motivation is to offer a succinct, beginner-friendly and structured way of introducing USD, its concepts and terminology. At the moment this book focusses predominantly on terminology, but will be expanded upon with workflow examples in the future.

You may be wondering at this point

Why read this book instead the official USD Glossary and Tutorials?

Jumping head-first into the official USD Glossary or Tutorials is a perfectly valid strategy; unfortunately, the reality is that USD is a very complex machine with many moving parts intertwined with one another. Simply trying out USD’s bundled applications like USDView can be quite involved if you’re not a software engineer.
Many concepts are also tied to the hip, as such it can be very easy to drown in terminology. This kind of “rabbit-hole-diving” method of learning can be incredibly confusing and overwhelming.

This book offers an alternative path to that.


Author’s Note

At Remedy Entertainment, we absolutely USD! We even gave a talk about how we are using it to make AAA games!

After the aforementioned presentation, we were asked to release our internal documentation on USD itself publicly as we had written a more simplified subset of the official USD Glossary. This book is that documentation, albeit cleaned up, rewritten and adjusted for mdbook compatibility.

We are releasing this with a permissive license, so you are free to fork/copy/change your version of this book as you see fit.

So, what even is USD?

In short, USD is a way to describe 3D-Scene Hierarchies.

Example USD Scene

Animal Logic ALab USD Scene, inside USDView

Generally speaking, hierarchies consist of entries (aka. nodes) that have parent/child/sibling relationships with other nodes.
In USD, these nodes can have “physicality” (think of Geometry, Animation data, etc…) or be more abstract like grouping items, materials, shaders, and even “settings” can be expressed as a node within a hierarchy.

Where USD differs greatly from other scene descriptions, is in its ability to combine hierarchies together with varying types of behavior (this is known as composition). Within USD you can non-destructively refer to- or graft other hierarchies together, re-define previously defined data, collaborate with many others on the same hierarchy, and much more.

Example USD Scene Composition


Part of the list of USD scenes that make up the composition of Animal Logic ALab USD Scene, inside USDView

But wait! There’s more!

Beyond non-destructively combining hierarchies, USD is built to be extensible.
For instance, extending USD with a plugin that can parse a bespoke or commercially available file format and use those files directly inside USD.

A concrete example would be to support using FBX files directly inside a USD hierarchy

All in all, USD aims to be a portable interchange format by defining a shared “language” sort to speak to describe scene and 3D data.
This idea is not new, but USD takes this a step further by also offering a way to consistently draw/render/image scenes. This is made possible through Hydra, USD’s backend rendering technology.

This book does not currently cover Hydra concepts, but may do so in the future

What is it not?

Just a File Format

While the main “interface” of working with USD for many artists is through its file-on-disk representation, it’s important to consider USD more as an ecosystem than another file type on disk.
Given its abilities and what it can offer, calling it a file format would be a disservice to the technology.

USD can be molded to your needs or it can be used as-is. File-on-disk representations can be omitted altogether. The way you can refer to other USD scenes is extremely flexible. And you can even integrate any custom rendering engine via a simple plugin so it understands how to work with USD data, etc…

Hot tip!

If you use USD enough, it becomes a way of life 😄

A Replacement for Content Creation

USD can do a lot of things, but it also cannot do quite a few other things. A major component of its architecture does not allow for interactive values. Meaning that values are resolved essentially “only once” and do not change unless the entire scene is “re-composed”. In reality the way values are computed is a bit more complex than the aforementioned, but it’s easier to think of it this way.

From a design point of view, USD was built with “low-memory footprint, higher-latency data access” in mind. Thus, in order to allow for low memory usage, data access is slower than in a “high-memory footprint, low-latency access to data” paradigm. The latter sacrifices memory footprint in favor of fast data access, allowing for dynamic evaluations.

Because of this design decision, USD cannot easily express “dynamic” systems like Rigging which relies on fast dynamically computed data access. It can however via its extensibility allow studios to define rigging concepts. But this is merely a specification, the rig itself would not be evaluated inside USD and will require the host software (for example Autodesk Maya) to take that information and translate it into a rig that it can understand.


Getting USD

USD is an amazing technology stack, but unfortunately that is also what makes it hard to get started with. It is a suite of libraries and tools that are all part of the larger ecosystem that makes up USD. And while third party tooling is getting better and better, just “grabbing USD” can prove to be quite challenging.

Pixar Animation Studios offers the source code on GitHub, and for developers that is generally the first point of entry to get started. They would clone the repository, bootstrap the right version of Python and their favorite C++ compiler and then take a long coffee break to get all the tools and libraries built for their system.

Suffice to say, this approach isn’t for everyone. Fortunately, it is also not the only way to get started with USD.

As a newcomer, there are generally two ways in how to get and use USD.

Within a Host Application

Host Applications are third party software that choose to embed USD into their toolset, or be built entirely on top of USD.

Below is a small list of the most well known applications that do so:

Standalone

This is at the moment the least artist-friendly option, but can be interesting for those who wish to experience USD as it is provided by Pixar Animation Studios. There are two ways of getting to use USD in a standalone manner:

  • Prebuilt binaries
    While there are few prebuilt binaries available, NVIDIA does provide some at developer.nvidia.com/usd

  • Compile-it-yourself
    This is the “traditional” way of getting started with USD and can be as mentioned incredibly daunting for non-software engineers. But, if you do wish to go down this route; Pixar Animation Studios provide fantastic build instructions

If you are new to USD, and you want to learn to construct and work with USD; it’s recommended to build USD compositions and layers through a Host Application. Each host does have its own learning curve and individual challenges and idiosyncrasies with regards to USD; but will likely get you going the fastest.

The Compile-it-yourself method is used for all images within this book, and relies heavily on bundled USD tools such as usdview.

USD Terminology

With introductions out of the way, the following chapters will focus exclusively on the core terminology needed to be able to understand USD. It will be much easier to follow any tutorial on USD if you understand the fundamental jargon first.

It is recommended to read the pages within this chapter in order.

Warning

The contents within this chapter do not aim to be exhaustive as many concepts are overly simplified and others are simply missing.

This is in other words not a replacement for the official USD Glossary and it is strongly advised to read those pages afterwards.

Prims

Short for Primitive, and a quintessential component of USD.
Prims are the nodes within a hierarchy and can thus have parent/child relationships with other prims; meaning that prims can have other prims as children or siblings, or have another prim as a parent.

In the image below, every node in the hierarchy is a prim.

Prim Example

Prims in USD View

The keen-eyed will notice that prims can have a type. Xform, Mesh, Scope and Material are specific prim types. These types come with default behavior and “data”, the mechanism of which is explained in a later chapter.

Users are also able to define their own Prim types

While Prims themselves indicate what “type” of scene element they are, they do not necessarily possess data themselves. However, they can be considered “containers” for named data, this data is generally expressed as Properties.


Properties

Prims can have Properties, which are essentially named and typed data.

A Property within USD is actually a collective term for two distinct types of Properties:

Attributes

Attributes are Properties with direct values that may vary over time

Relationships

Relationships are Properties that point to other properties or Prims

In the above, you can see that properties are made up from a name and a typed value

These property names can also be namespaced. A property name can have 1 or more namespace identifiers separated by :.
Looking closer at the relationship example, the property name material:binding is actually namespaced. The property’s name itself is binding and it is part of a material namespace.

Namespaces can be used to categorize or group properties together.


Path

Prims and properties are identified by unique paths inside the scene hierarchy. They are a textual representation of a hierarchy - similar to folder paths in most operating systems - where each prim is separated from its parent or child via the / delimiter.

Just like folders, paths can be relative or absolute. Absolute paths always start with /

/ is a special path in USD. This is known as the PseudoRoot

In the example below, the highlighted path /root/remi/head_M_hrc/GEO/head_M_hrc/eyeScrew_L_geo is a path to a prim with the name eyeScrew_L_geo. Constructed from the following hierarchy: /rootremihead_M_hrcGEOhead_M_hrceyeScrew_L_geo.

Prim Path Example

As for properties, taking the previous example, inspecting the points attribute yields the path /root/remi/head_M_hrc/GEO/head_M_hrc/eyeScrew_L_geo.points. Property paths are constructed by appending the property name to a prim path, delimited by a . character.

Property Path Example

Paths in USD are name based, and this means you cannot define the same path twice. In practice this means that you cannot have two or more sibling prims with the same name.


Layer

A Layer is a collection of Prims and their Properties that can be saved to/loaded from disk or memory. As such, it can be considered a “saveable hierarchy”.

Standard USD Layers can be represented on-disk via

ExtensionDescription
.usdaASCII Text, human-readable format
.usdcUSD Crate file format. High performance binary not human-readable format
.usdeither Crate or Text
.usdzuncompressed and packaged format (.zip)

USD allows for extending what you can load as a “Layer” via a special kind of plugin (SdfFileFormat). In fact, the file types listed above are actually all plugins of this type.
Using this SdfFileFormat plugin type, it is for example possible to also support loading .fbx, .abc, .obj (or anything really) in Usd.


Metadata

Prims, their properties and the layers they are part of can have metadata applied to them. This is additional static (as in, it can not change over time) data that USD or a user can read, use or define.

Metadata can be used to describe behavior, incur meaning, represent documentation, etc. USD comes bundled with an extensive suite of metadata out of the box.

Developers can define new metadata via USD Plugins!

Simple Metadata Examples

Layer Metadata

StageMetadata

In the image above, the PseudoRoot at path / has been selected and the metadata panel in usdview has been highlighted. This root represents the entry.usda layer from Animal Logic ALab. entry.usda defines metadata that can be inspected and presents the following information to the user about itself:

  • The linear units used are 0.01 meters (i.e. centimeters) → metersPerUnit = 0.01
  • The scene’s up axis is YupAxis = Y
  • Two layers contribute to the scene (more about this later in Local/Sublayer) → subLayers = [...]
  • Animation is present between time code 1004.0 and 1057.0 with 24.0 frames and time codes per second.
    • startTimeCode = 1004.0
    • endTimeCode = 1057.0
    • framesPerSecond = 24.0
    • timeCodesPerSecond = 24.0

Do note that while a lot of metadata is just informational data, a large part of the core metadata suite also has side effects that contribute to how USD works internally.

For example, metersPerUnit is purely informative, whereas subLayers has profound effects when edited!

Other USD hosts like Autodesk Maya or Sidefx Houdini may choose to interpret and act on metadata where USD itself does not

Prim Metadata

StageMetadata

Metadata on Prims can infer a lot of information about the prim itself, how it is used in the scene (aka Composition), what kind of properties it has, etc.

From the above we know that the prim at /root/alab_set01/lab_electronics01_0001/bench01/decor_paper_notej01_0001

  • Is an XformtypeName = Xform
  • It is instanced (more about Instancing later) → instanceable = true
  • Has two VariantSets (more about VariantSets later) → geo and geo_vis
  • Is categorized as a component (more about the Kind metadatum later) → kind = component
  • Etc.

Property Metadata

StageMetadata

Lastly, there is property metadata. This data is meant to give fine grained information about the property itself. For example

  • What type it is → typename = double3
  • Whether it can change over time → variability = Sdf.VariabilityVarying
  • Is it a custom “out-of-schema” (see Schemas later) property → custom = false

Layer Stack

Each layer internally keeps track of a “local” stack of layers that contribute to that layer. This stack is an ordered collection of layers used within this layer that contribute to its hierarchy and composition.

furniture_workbench01.usda layer stack

In the above, the layer furniture_workbench01.usda is made out of three other layers and itself. It adds the layers, furniture_workbench01_modelling.usda, furniture_workbench01_surfacing.usda and furniture_workbench01_rigging.usda in that order to its layer stack. Visually this order will look reversed as it is a stack after all, but the higher on the stack, the more “important” a layer is.

A layer’s Layer Stack always has its own data at the top of the stack, making itself the most important.


Composition

Roughly put, the act of combining layers and their prims, properties and metadata together through various means known as composition arcs.

While combining layers together could be considered “composing” them, what USD’s composition engine actually uses are the layer stacks of each layer.

This is a very important distinction to make as targeting a layer stack also means that any composition within the target layer stack is also taken into account

composition of furniture_workbench01.usda’s /root prim

The example may look complex and confusing, but all it says is that the prim at path /root has contributions from all the layers (specifically, their Layer Stacks) listed.
The Arc Type column indicates how those layers contribute to the final “specification” of /root (more on that later).


Stage

The composed result of opening a layer. Think of it as a Photoshop canvas when opening a .PSD document. All the Photoshop layers interact with one another to produce a “final image”. The layers still exist, they can still be edited, deleted, replaced, etc… but what you see is the result, and that is the Stage in USD.

This composed result can be “flattened” and saved to a new layer, which would be similar to taking the aforementioned Photoshop document and exporting it to a JPEG.


Opinions

Circling back to properties, when you set a value on an attribute or metadatum or author a relationship within a layer, you are effectively expressing an opinion on that value. This is named an opinion because value assignments can always be changed by others! This is a very important concept for composition.

A different way of looking at this would be that “setting a value” implies that that action is final and the value itself becomes immutable; whereas with opinions you merely mean

“Within this layer, I want the value for this to be <INSERT VALUE>

If the value already existed before, you are essentially “overriding” it


Overrides

When expressing an opinion within a layer, it is possible to “redefine” a previously defined value of the property being edited. This mechanism is known as overrides because you are overriding what was there before. However, it is very important to note that the original data remains unchanged. The override only exists within the layer where you are defining it

This is by far the most important aspect to understand about USD. Opinions and their “value resolution” (resolving which value gets applied in the end) are key to the entire composition mechanism.

Below is a simple example of overriding a previously defined attribute’s opinion.

override example

schematic

  1. Shows cube_sphere_torus.usda where an attribute’s opinion is first authored (/GEO/Cube.size)
  2. Shows referenced.usda where
    • The layer cube_sphere_torus.usda is brought into the layer referenced.usda via a composition mechanism called referencing
    • An opinion is expressed on the already defined attribute /GEO/Cube.size, but in context of cube_sphere_torus.usda, essentially overriding what was there before
  3. Shows the composed final result, aka the stage

Stage Traversal

Generally a concept used when programming with USD, but it is important to understand for users too. Stage Traversal is essentially iterating over the scene graph of a layer opened in a stage. Traversal can be filtered via “rules” (or predicates), limited to sub-trees, parts of the hierarchy can be pruned during traversal, and so on.

By default, USD Stage Traversal will only consider prims that are active, defined, loaded and are not abstract. Depending on which tool is used, users may be able to change this behavior. Using the programming interface however, developers have full control over this behavior.

Toggling Abstract prims in USDView

The prim _root_type is defined as a class, which is considered abstract in USD. When opening the layer it is part of in USDView, it will not be shown in the hierarchy.
However, enabling Abstract Prims (Classes) in the Show menu of the hierarchy view, any abstract prims like _root_type will then be displayed in the hierarchy. Internally, this is modifying the Stage Traversal predicate.


Kind

Kind is a prim-level metadatum that can be used to “categorize” prims and their descendants into higher level concepts than just their definitions (ex. Mesh, Sphere, Cube, etc…).

Example Applied Kind

kind

USD ships with a few different kinds that can be used to categorize prims

Kind NameWhat it does
modelbase class for all model kinds. model is considered an abstract type and should not be assigned as any prim’s kind
groupmodels that simply group other models
assemblyan important group model, often a published asset or reference to a published asset
componenta “leaf model” that can contain no other models
subcomponentan identified, important “sub part” of a component model

You can add new kind types via USD Plugins!

Using kind can greatly speed up finding the “right” types of prims you may be interested in during Stage Traversal. Kind annotations can be used to selectively prune entire prim child-trees for instance.

How kind can be used

Taking a look at the root layer of Animal Logic ALab, we can see that top-level prims can be annotated as assembly, this allows for easily identifying “assets” within the scene. In the example below, both lab_workbench01_0001 and its parent alab_set01 are annotated as assemblies.

lab_workbench01_0001

An assembly annotated prim by itself can contain other prims with varying types of kinds. Looking deeper in the example above, we can see that the workbench itself at /root/alab_set01/lab_workbench01_0001/workbench01 is marked as a group.

workbench01

And diving even deeper, /root/alab_set01/lab_workbench01_0001/workbench01/furniture_workbench01_0001 is a component. Component should not contain any other kind types that derive from model, but it can have subcomponent kinds.

furniture_workbench01_0001

These components can then be used for efficient discovery and instancing without having the need to dive deeper into their own hierarchies.

Production environments differ, so one studio’s idea of an asset may differ from another, but the idea of kind based categorization is incredibly valuable


Purpose

Purpose is an attribute that can be used to give a prim and its descendants a high-level “visibility flag” in context of rendering.

For example, if a prim has its purpose attribute set to render, it will be excluded from being drawn in a renderer that only wants to draw proxy prims.

In some sense, setting purpose could be considered Stage Traversal but for rendering.

Currently, only 4 values are supported by the purpose attribute:

PurposeDescription
defaultThe prim has no special rendering purpose and it will be included in all rendering paths
guideA prim tree marked with guide is generally used by interactive applications that have asked to show “guides”. Think of it as requesting to visualize controller geometry for rigs, skeleton data, etc
proxyProxy is usually reserved for a lightweight representation of another object to be used in an interactive renderer such as a DCC viewport
renderThe “final quality” data to be imaged. Usually enabled for offline rendering or final quality rendering

Unlike Kind. purpose cannot be extended with custom values!

Example Switching between proxy and render Purposes

purpose

As seen above, purpose is not an exclusive toggle! It is possible to have both proxy and render purposes active and their contents drawn to the screen.

/root/GEO has its purpose set to render, it will only be drawn when the active renderer requests prims with a render purpose.

Render Purpose

renderpurpose

/root/GEO_PROXY has its purpose set to proxy, it will only be drawn when the active renderer requests prims with a proxy purpose.

Proxy Purpose

proxypurpose


Specifier

When defining a prim in the scene graph, there are three different ways of doing so.

The examples below use the text representation of USD as these concepts are not well visualized in usdview

def

A concrete definition. This is generally used when you want to define a prim for the very first time, or when a prim needs to be redefined. defs always contribute to scene composition.

def Xform "MyTransform"
{
  # ...
}

def can also be type-less and is usually used in context of composition, which will fill in the actual type.

def "MyPrim" {
  # ...
}

over

This tells USD that a prim is to be overridden. If the composition engine cannot find the prim targeted by the over specification, the override will be ignored and will not contribute to scene composition.

over "SomethingThatAlreadyExists"
{
  # ...
}

class

An abstract prim definition that does not contribute to stage traversal by default. Meaning, it does not show up in the stage hierarchy. Other prims can however, inherit from this class (more on that in the next chapters)

class "_MyClass"
{
  # ...
}

Composition Arcs

USD’s composition engine utilizes a few different ways of composing, these are known as Composition Arcs. Simply put, they are a set of operators that determine how layer stacks and their opinions are combined together.

Let’s look in more depth about each different composition arc type.


Local/Sublayer

Layers can sublayer other layers, grafting multiple layer stacks together in order into one. Sublayering is in fact how Layer Stacks are made!

Order matters when sublayering.

To emphasize how order matters, given a Cube prim at path /Foo in layer_a.usd and a Sphere prim at path /Foo in layer_b.usd; when sublayering both in layer_c.usd, the final type of /Foo will depend solely on the order in which layer_a.usd and layer_b.usd are brought in.

Simple Sublayering Example

Below we have three layers, a_cube.usd, a_sphere.usd and cube_and_sphere.usd

a_cube and a_sphere define a Cube and Sphere prim respectively. cube_and_sphere does not define either but sublayers both previous layers, essentially combining the two hierarchies into one defined in cube_and_sphere’s layer stack.

Sublayering grafts layers together at their roots! If you wish to combine layers at different levels of a hierarchy, use the reference composition arc


Reference

Layer References

Beyond sublayers, layers can also be grafted underneath prims. Using the example Cube and Sphere from Local/Sublayer, we can also create prims and have them point to these layers respectively.

Layer referencing

reference_1 reference_2

Local References

While the general use-case is to use layers when referencing, it is not required to do so. It is entirely possible to add a reference to a prim within the same layer. This is known as a local reference

Local referencing

reference_3 reference_4

You would merely need to add a reference to the target prim and its entire hierarchy will be grafted underneath:

def "ReferenceToFoo"(
        prepend references=</foo>
) {
}

Layer Prim References

But, this goes even further! Remember, composition acts on layer stacks, not individual layers; so it is also possible to reference specific prims from another layer’s layer stack. Even if these prims themselves are the composed result from the target layer’s layer stack

Layer Prim referencing

reference_5 reference_6

def "ReferenceToPrim"(
    prepend references= @./several_prims.usd@</A_Capsule>
) {
    float3 xformOp:translate = (3, 0, 0)
    uniform token[] xformOpOrder = ["xformOp:translate"]
}

VariantSet

VariantSets and variants allow for authoring a “switchable” state of a prim within a layer. In the example below, a top level prim with the name Implicits is authored which defines a VariantSet named Shape. This VariantSet defines a few different entries (aka variants ) that each do something different.

When changing the active variant, USD will inject anything that is defined within that variant into the current composition. In our example, each variant adds a child to Implicits with differing types. One is a capsule, another a cube, a sphere, and so on.

Variant/VariantSet example

Variant Sets

Within variant definitions, you are essentially free to do whatever you want. If you wish to define a complete hierarchy in a variant, you can. Add references to other layers? You can. Expressing opinions on values or metadata, you can. However.

There can only ever be one Variant active at a time for a VariantSet.


Payload

Payloads are lazily loaded references. When a prim uses a payload, USD can be told to opt out of loading these layers unless the user requests to do so.

When a referenced layer is heavy due to the amount of data or hierarchy complexity, it is often beneficial to have the user manually load those in after opening the layer in a stage. Generally speaking, non-important layers should be behind payloads. If they’re not loaded, they also cannot be traversed and should not contain anything considered “important”.
The definition of “important” depends entirely on the use case of course.

Example → Putting a high res model behind a payload

With unloaded payloads, the resulting hierarchy looks like this

Unloaded Payload

unloaded

However, the payload can be toggled on/off after the stage has been loaded.

Payload Toggle On/Off

loaded

As a result, the full hierarchy is loaded

Loaded Payload

unloaded

simple_payload_example.usd

#usda 1.0
(
    defaultPrim = "Asset"
)
 
def Scope "Asset"
{
    def Xform "Geometry"
    (
        prepend payload=@./highres_model.usd@
    )
    {
    }
}

Inherits

Very similar to the concept of “inheritance” in programming. Prims can “inherit” other prims (concrete or abstract), meaning that they reflect the hierarchy and properties defined in the prim that they are inheriting from. This also means that if a change is authored on the base prim or its hierarchy, any prims that inherit it immediately get those changes applied to them.

Within context of a singular layer, inherits does not offer a noticeable difference between it and a local reference. However, when using inheritance in a multi-layer composition environment, it does differ. References tend to become fully encapsulated within the layer stack they’re referenced. Meaning that references loose any sense of “connectivity” to their referred layers outside of their layer stack. Inherits on the other hand are always live, no matter how deeply nested within a composition, or how many redefinitions of its base. A prim is always able to backtrack to its original “base”, regardless of composition depth!

Prims cannot inherit from an ancestor or descendant, inherit “bases” should be defined as siblings or outside of the target prim’s hierarchy, for example at the root level

Example → Simple inherits

SomeScope, SomeMesh and SomeTransform are defined prims of differing types, but all inherit from an abstract class prim named _Base . This class prim defines two Xform children, Foo and Bar.
Due to _Base being a class, it does not automatically show up in the hierarchy as class prims are not taken into account in default Stage Traversal. So we have to explicitly tell USDView to also display Abstract Prims

Definition of _Base

Inheriting from _Base results in having its hierarchy grafted underneath each prim that inherits it.

Simple inherits

Modifying _Base would be reflected immediately on all prims that inherit from it, without having to do anything to these prims. In this case, a new child prim was added under /_Base/Bar called Baz

Modified _base


Specializes

Specializing is taking a base definition of a prim (and its hierarchy) and further refining it into a specialized version of itself. For example taking a Metal material and specializing certain properties to make a CorrodedMetal material. This specialization could refine certain material properties, redefine or add shaders, textures, etc… It is not dissimilar from inherits, however with one key difference.

Opinions expressed as specializations always win

If you try to express an opinion on a specialized property, your opinion will essentially be discarded. This is not the case however with inherits.

You cannot specialize an ancestor or descendant of a prim. Only siblings or prims outside of the Specialization’s definition are allowed

Example → Corroded Material specialization

There are two layers, robot.usd and world.usd.

Robot.usd

robot.usd defines a Robot prim and three Material prims, Metal, CorrodedMetal and InheritedMetal. Metal defines two attributes, inputs:diffuseGain and inputs:specularRoughness.

robot.usd

CorrodedMetal is a specialization of Metal where inputs:specularRoughness is the specialized attribute, we say it always has a value of 0.2.

specialized metal

def Material "CorrodedMetal" (
    specializes = </Robot/Materials/Metal>
)
{
    # specialize roughness...
    float inputs:specularRoughness = 0.2
}

Whereas InheritedMetal is merely an inherits arc to Metal . You will notice that regardless of inherits or specializes, the result is the same. Both CorrodedMetal and InheritedMetal take over the base properties of Metal, but have an opinion on the inputs:specularRoughness attribute.

inherited metal

world.usd

world.usda references robot.usda and expresses and opinion on the base Metal prim. It says that inputs:diffuseGain and inputs:specularRoughness are to be 0.3 and 0.1 respectively.

world.usd

#usda 1.0
 
def Xform "World"
{
    def "RosieTheRobot" (
        references = @./robot.usda@</Robot>
    )
    {
        over "Materials"
        {
            over "Metal"
            {
                    float inputs:diffuseGain = 0.3
                    float inputs:specularRoughness = 0.1
            }
        }
    }
}

Difference between Inherits and Specializes

When inspecting the resolved opinions in world.usd, notice that the overridden values for inputs:diffuseGain and inputs:specularRoughness on the base Metal propagate fully to InheritedMaterial, but not to our specialized CorrodedMaterial.

Base Metal OverrideResult on InheritedMaterialResult on CorrodedMaterial
over “Metal”
{
    float inputs:diffuseGain = 0.3
    float inputs:specularRoughness = 0.1
}
../images/terminology/specializes_3.png

As seen above, even when we have a stronger opinion on the base Metal definition for both properties, specializations always win.


Strength Ordering (LIVRPS)

Composition and opinion value resolution go hand in hand, and in order to remain sane; there are rules. Each composition arc has a “strength” assigned to it. Meaning that if for example opinions targeting a property are authored on 3 different composition arcs within the same layer stack, the composition arc that is the “strongest” wins. This is where the acronym LIVRPS (Liver Peas) comes into play.

It stands for

  • Local
  • Inherits
  • Variants
  • References
  • Payloads
  • Specializes

This list acts as top-down stack. When USD needs to resolve an opinion for a property or metadatum, it will look for opinions via these composition arcs in this exact order. If an opinion has been found, any subsequent searches down the stack are immediately aborted and the opinion is resolved. If no authored opinion has been found, USD will fall back to the property/metadatum’s default value.

All composition arcs besides Local will trigger a recursive LIVRP(S) evaluation. However, within this recursive evaluation, S(pecializes) is ignored except for the Specializes composition arc. It triggers a full LIVRPS evaluation if encountered.

The following diagram visualizes the decision tree of when and where opinion values are fetched


Default Prim

When authoring a layer, it is possible to also define a “default prim” for it. This is a layer metadatum that contains the name of the prim that should be used automatically when the layer is used in a reference or payload composition arc without an explicit prim path.

This name must point to a root level prim, meaning a direct child of the PseudoRoot. Therefore, it is not allowed to point to a path.

example defaultPrim in layer metadata

#usda 1.0
(
    defaultPrim = "PrimName"  
)

def "PrimName" {}

Invalid defaultPrim

#usda 1.0
(
    defaultPrim = "PrimName/NestedPrim"  
)

def "PrimName" {
    def "NestedPrim" {}
}

If defaultPrim has not been explicitly authored, USD will trigger a “unresolved reference prim path” warning and the target layer will not compose correctly.

Warning: ... In </reference>: Unresolved reference prim path @.../defaultPrim.usda@<defaultPrim> ...

It is recommended to always specify defaultPrim on layers that will be used as references or payloads

Example

Let’s take a look at a very simple example case. There are two layers, one that defines multiple prims at its root (defaultPrim.usda), and one that references it (reference.usda).

defaultPrim.usda

#usda 1.0
(
    defaultPrim = "CubePrim"  
)

def Cube "CubePrim" {}
def Sphere "SpherePrim" {}
def Cone "ConePrim" {}

Do note that reference.usda only references the layer itself and does not specify a particular prim to reference in from defaultPrim.usda

reference.usda

#usda 1.0

def "reference"(
    prepend references= @defaultPrim.usda@
) {
}

When opening the reference.usda layer inside a usdview stage, we see that only one prim is brought into its Layer Stack, and that is the Cube.

When changing the default prim metadatum to for example defaultPrim = "SpherePrim", the Sphere will now be referenced in instead.

Removing the defaultPrim metadatum altogether will yield an empty reference arc. The layer itself is still referenced, but none of the prims in it are composed.


↪ No USD Glossary Entry!

List-Editing

List editing is a feature within USD that allows for non-destructively editing list-typed elements within a scenegraph. You can append, prepend or delete items from a list during composition. Or even explicitly override the entire list. This is generally referring to references, relationships, custom metadata, inherits, specializations and variantsets. Attributes of a list-type cannot be list-edited.

In the example below, we override the list of references on a prim to also prepend a new reference to another layer. This results in the prim referring to two layers (assuming the original definition only points to a single layer).

Example list-editing references

#usda 1.0
 
over "World"
{
    over "Foo"
    {
        # Override "Bar" to also reference baz.usd in addition to its original references
        over "Bar" (
            prepend references = @./baz.usd@
        )
        {
        }
    }
}

EditTarget

When opening a layer in a stage, and you make edits to the scene graph, these edits are by default recorded onto the “Root Layer” (the layer used to create the stage).

In the scenario of a complex composition consisting out of many layers and composition arcs, it is not out of the ordinary to want to make edits to one of those layers. Rather than opening said layer individually and applying the edits there, USD offers something called an EditTarget. This EditTarget allows you, as the user, to specify to USD which layer edits should get recorded to (edits being: overrides, hierarchy changes, etc…).

With this mechanism users can work in a fully composed scene, yet still be able to record edits to individual layers used in this composition. Think of it as changing the active layer in a Photoshop document.

This is a core workflow in USD

Note

Usd by default also creates something called a “Session Layer” when creating a stage.

This is a “scratch space” that can used as an EditTarget where users can record any edits without targeting any of the original layers.

This Session Layer sits at the top of the stage’s layer stack (stages also have layer stacks) and acts as any other layer in a layer stack. If set as an EditTarget, opinions expressed in this session layer are always the strongest.


Instancing

Instancing is a mechanism in USD where layers can be designed to save memory and composition complexity. When a layer is for example referenced onto a prim, USD will re-compose the entire hierarchy for each reference in that layer, even if all references point to the same layer.

With instancing however, USD will create a “Prototype” of this reference’s composed result behind the scenes, and every reference to this layer is replaced by a local-reference to this prototype prim instead. Enabling USD to compose the referenced layer’s layer stack only once.

This concept does however come with a caveat, overrides defined on prims underneath the prim that holds the reference are ignored.

Many bottles reference the same layer

All selected prims in the example above reference chemistry_bottle01.usda, but are also marked as instanceable via the instanceable = true metadatum. This metadatum tells USD that it should only compose the chemistry_bottle01.usda once in a __Prototype prim. When having done so, all references internally to chemistry_bottle01.usda are actually local prim references to the prototype prim (in this case __Prototype_37).

bottle prototype prim

When a prim is marked as an instance, Stage Traversal will also halt at this prim!


Schemas

Schemas are the “descriptions” of Prims, properties, metadata, etc… Schemas allow USD to have “knowledge” of what a Prim or its attributes mean. USD can be extended with new schemas via Plugins to define custom sets of properties, whole new prim types, metadata, and more.

Schemas are built by authoring the schema definition itself in the .usda file format syntax, which can be used to generate the necessary source code and plugin files.
Without diving into the particulars, USD’s default prims and properties are in fact Schemas that Pixar has provided in the source code.

Since USD 21.08, schemas do not require to generate code to be usable, so only the plugin is needed. These are known as codeless schemas

Generally there are two types of schemas users can author.


IsA (Typed) Schema

“Is A prim this”-kind of schema. Essentially the schema definition that defines the “type” of prim.

Prim Type Schema

In the image above, the Type column lists which IsA Schema the prim subscribes to.

When looking at the USD “code”, the keyword between def and the name of the prim signifies which IsA Schema the prim should subscribe to.

def Xform "MyTransform" {}  # MyTransform subscribes to the Xform IsA Schema
def Cube "MyCube" {}  # MyCube subscribes to the Cube IsA Schema
def "EmptySpec" {}  # EmptySpec is a valid prim, it simply has no default set of properties attached to it. It could literally be anything.

Prims cannot subscribe to more than 1 IsA schema!

IsA Schema Example -> UsdGeomCube

In the example below you can see the schema definition for our favorite Cube. It defines that Cube inherits from Gprim (a base for all drawable prims), metadata for the type (like documentation string) and two attributes. size and extent.

Cube schema definition

class Cube "Cube" (
    inherits = </Gprim>
    doc = """Defines a primitive rectilinear cube centered at the origin.
    
    The fallback values for Cube, Sphere, Cone, and Cylinder are set so that
    they all pack into the same volume/bounds."""
    customData = {
        dictionary extraPlugInfo = {
            bool implementsComputeExtent = true
        }
    }
) {
    double size = 2.0 (
        doc = """Indicates the length of each edge of the cube.  If you
        author \\em size you must also author \\em extent.
        
        \\sa GetExtentAttr()"""
    )

    float3[] extent = [(-1.0, -1.0, -1.0), (1.0, 1.0, 1.0)] (
        doc = """Extent is re-defined on Cube only to provide a fallback value.
        \\sa UsdGeomGprim::GetExtentAttr()."""
    )

}

Creating new prim types can be as easy as the above, or as complex as defining an entire Mesh.


API Schemas

API Schemas offer a way to contribute to a prim’s “definition” by defining sets of predefined properties and metadata or offer a way to extract that information for you.

Generally speaking, API Schemas come in two different flavors, Non-applied API Schemas and Single or Multiple-applied API Schemas.

Non-Applied Schemas

These schemas helps the developer by offering a high level abstraction for fetching properties and metadata.

These kinds of schemas usually do not define properties themselves, only ways to fetch them.

# Python example to set the Kind type on a prim via the UsdModelAPI, a non-applied API schema
Usd.ModelAPI(prim).SetKind(Kind.Tokens.subcomponent)

Users will generally never have to deal with these as they can only really be used programmatically

Single or Multiple-applied API Schemas

These are the kinds of schemas users will interact with implicitly.
Applied API Schemas define the default properties that will be added to a prim’s definition when it is applied.

For example if an applied schema (let’s call it FooAPI) defines a float attribute named foo, when that schema is applied to a prim, the foo property will be available for users to express opinions on. If the user does not express an opinion on it, foo will fall back to its default value.

In a more concrete example, let’s look at the MaterialBindingAPI which is responsible for defining properties on prims that need a binding to a material.
The prim at path /Sphere/Mesh has the MaterialBindingAPI applied to it through the prim’s apiSchemas metadata (this a List-Editable metadatum btw, so users can also override which API Schemas are applied to a prim)

Applied Schemas

This allows for editing the material:binding property on this prim now. The important thing to note here is the custom metadatum on the property. This indicates that the property is known to USD and that it belongs to a schema. Generally speaking, if custom is authored as True, it is considered an inert, out-of-schema property.

Schema defined attribute