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
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.
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:
- 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.
- Publications showing images derived from these digital assets must include the above copyright notice.
- 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.
- 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.
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.
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.
So, what even is USD?
In short, USD is a way to describe 3D-Scene Hierarchies.
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.
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…
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.
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:
- NVIDIA Omniverse Create - A very capable USD assembler built on top of NVIDIA’s Omniverse which brings you very close to working with raw USD
- Autodesk Maya + MayaUsd - Professional 3D Software, the Maya USD integration is an add-on that can be installed during installation
- SideFx Houdini + Solaris - Professional 3D Software, the look development suite called Solaris is built-in and built on USD
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.
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.
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:
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: /
→ root
→ remi
→ head_M_hrc
→ GEO
→ head_M_hrc
→ eyeScrew_L_geo
.
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.
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
Extension | Description |
---|---|
.usda | ASCII Text, human-readable format |
.usdc | USD Crate file format. High performance binary not human-readable format |
.usd | either Crate or Text |
.usdz | uncompressed 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
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
Y
→upAxis = Y
- Two layers contribute to the scene (more about this later in Local/Sublayer) →
subLayers = [...]
- Animation is present between time code
1004.0
and1057.0
with24.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
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
Xform
→typeName = Xform
- It is instanced (more about Instancing later) →
instanceable = true
- Has two VariantSets (more about VariantSets later) →
geo
andgeo_vis
- Is categorized as a
component
(more about the Kind metadatum later) →kind = component
- Etc.
Property Metadata
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.
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
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.
- Shows
cube_sphere_torus.usda
where an attribute’s opinion is first authored (/GEO/Cube.size
) - Shows
referenced.usda
where- The layer
cube_sphere_torus.usda
is brought into the layerreferenced.usda
via a composition mechanism calledreferencing
- An opinion is expressed on the already defined attribute
/GEO/Cube.size
, but in context ofcube_sphere_torus.usda
, essentially overriding what was there before
- The layer
- 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.
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…).
USD ships with a few different kind
s that can be used to categorize prims
Kind Name | What it does |
---|---|
model | base class for all model kinds. model is considered an abstract type and should not be assigned as any prim’s kind |
group | models that simply group other models |
assembly | an important group model, often a published asset or reference to a published asset |
component | a “leaf model” that can contain no other models |
subcomponent | an 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.
An assembly
annotated prim by itself can contain other prims with varying types of kind
s. 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
.
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.
These component
s 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:
Purpose | Description |
---|---|
default | The prim has no special rendering purpose and it will be included in all rendering paths |
guide | A 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 |
proxy | Proxy is usually reserved for a lightweight representation of another object to be used in an interactive renderer such as a DCC viewport |
render | The “final quality” data to be imaged. Usually enabled for offline rendering or final quality rendering |
Unlike Kind. purpose
cannot be extended with custom values!
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.
/root/GEO_PROXY
has its purpose set to proxy
, it will only be drawn when the active renderer requests prims with a proxy
purpose.
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
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. def
s 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" {
# ...
}
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"
{
# ...
}
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.
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.
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
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
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.
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
However, the payload can be toggled on/off after the stage has been loaded.
As a result, the full hierarchy is loaded
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
Inheriting from _Base
results in having its hierarchy grafted underneath each prim that inherits it.
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
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
.
CorrodedMetal
is a specialization of Metal
where inputs:specularRoughness
is the specialized attribute, we say it always has a value of 0.2
.
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.
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.
#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 Override | Result on InheritedMaterial | Result on CorrodedMaterial |
---|---|---|
over “Metal”{ float inputs:diffuseGain = 0.3 float inputs:specularRoughness = 0.1} |
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
L
ocalI
nheritsV
ariantsR
eferencesP
ayloadsS
pecializes
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.
#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
).
#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
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).
#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
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.
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
).
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.
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
.
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)
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.