By Juliana Faux, Andrés Escobar and Santiago Ferreiros
Universal Scene Description (USD)
Pipelines capable of producing computer graphics films and games typically generate, store, and transmit large quantities of 3D data, which we call “scene description”. Each of many cooperating applications in the pipeline (modeling, shading, animation, lighting, fx, rendering) typically has its own special form of scene description tailored to the specific needs and workflows of the application, which is neither readable nor editable by any other application. Pixar introduced Universal Scene Description (USD) as the first publicly available software to robustly and scalably interchange and augment arbitrary 3D scenes.
USD organizes information in a structured way using building blocks called Prims. Each Prim can hold data called Attributes and Relationships to other Prims, known as Properties. Relationships define hierarchies or dependencies. All these Prims and their contents are grouped together in a file structure called a Layer. Layers allow different people to simultaneously work on the same asset or scene, by allowing each artist to work in their own file. A Stage is the result of combining different layers.
USD Glossary
Prims
A Prim refers to a fundamental building block or element of a scene within the USD framework. Prim is short for “primitive,” and it represents an individual object or entity within the scene hierarchy.
A USD prim can be thought of as a container that holds various types of data, attributes, and relationships. It represents a specific object or component in a scene, such as a model, camera, light, or even a group of other prims. Prims are organized in a hierarchical structure, forming a scene graph that represents the relationships and transformations between objects in the scene.
Each prim in USD has a unique identifier, known as a path, which identifies its location within the scene graph. The path typically includes the names of all the parent prims leading to the specific prim. For example, a prim’s path might be “/World/Characters/CharacterA,” indicating that it is a child of the “CharacterA” prim, which itself is a child of the “Characters” prim, and so on.
Prims hold various attributes, such as position, rotation, scale, material information, and animation data. These attributes define the properties and characteristics of the objects they represent.
In summary, a USD prim represents an individual object or component within a scene in the Universal Scene Description framework. It serves as a container for data, attributes, and relationships, forming a part of the scene graph. Prims are organized hierarchically, have unique paths, and allow for efficient encapsulation and sharing of data within the OpenUSD ecosystem.
Prim types
Prims have a type, which come with default behavior and data. Some of them are:
- Camera Prim: represents a camera, with properties such as focal length, lens aperture, etc.
- Light Prim: represents different types of light (e.g. DistantLight, which represents light emitted from a distant source along the Z axis, like the sun).
- Xform: represents a transformation in the space (translation, rotation and/or scaling).
- Material Prim: represents superficial properties of objects.
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”.
Stage
The composed result of opening a layer. All 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.
Example: A Park Scene
- Prim for a Tree
- Attributes: This tree Prim has attributes like height (10 feet) and color (green). These values can change over time, such as when the tree grows or during seasonal changes, like turning brown in the fall.
- Relationships: The tree may have relationships pointing to a ground Prim, indicating where it is planted.
- Prim for a Bench
- Attributes: This bench Prim could have attributes such as material (wood) and size (6 feet long).
- Relationships: It might also link to a location Prim that specifies its exact position in the park.
All these Prims—like the tree and the bench—along with their details, are organized within a Layer. This structure makes it easy to manage and update the scene. By combining different layers, you create a comprehensive representation known as the Stage.
Digital Assets
3D assets are the essential components of a digital twin, representing digital objects with accurate shapes, materials, lighting, physics, and relevant behaviors. These assets can also incorporate real-time data streams from internal or external sources, similar to physical objects equipped with sensors and IoT devices.
In NVIDIA Omniverse, Pixar’s open-source Universal Scene Description (USD) serves as the primary format for asset representation. USD offers a robust scene description framework with an API that supports complex property inheritance, instancing, layering, lazy loading, and a variety of other key features. Additionally, Omniverse allows the creation of different “variants” for each asset, enabling the randomization of an asset’s appearance, position, orientation, and behavior to achieve more realistic scenes.
Assets can be imported from Omniverse’s extensive asset libraries or created using external content creation tools and integrated into the Omniverse ecosystem. NVIDIA Omniverse™ provides all the necessary tools to quickly and easily convert assets to and from USD. Moreover, Omniverse offers a comprehensive library of industry-specific USD assets that you and your team can utilize to build your digital twin. For more information about assets and Omniverse support, click here.
3D assets
3D assets are the essential components of a digital twin, representing digital objects with accurate shapes, materials, lighting, physics, and relevant behaviors. These assets can also incorporate real-time data streams from internal or external sources, similar to physical objects equipped with sensors and IoT devices.
In NVIDIA Omniverse, Pixar’s open-source Universal Scene Description (USD) serves as the primary format for asset representation. USD offers a robust scene description framework with an API that supports complex property inheritance, instancing, layering, lazy loading, and a variety of other key features. Additionally, Omniverse allows the creation of different “variants” for each asset, enabling the randomization of an asset’s appearance, position, orientation, and behavior to achieve more realistic scenes.
Assets can be imported from Omniverse’s extensive asset libraries or created using external content creation tools and integrated into the Omniverse ecosystem. NVIDIA Omniverse™ provides all the necessary tools to quickly and easily convert assets to and from USD. Moreover, Omniverse offers a comprehensive library of industry-specific USD assets that you and your team can utilize to build your digital twin. For more information about assets and Omniverse support, click here.
Introduction to USD in Python
Before reading this section, is it strongly recommended to first read Exploring NVIDIA Omniverse and Isaac Sim
pxr and omni.usd
In Python, you can work with USD using the usd-core package, which is imported as pxr (Pixar). Additionally, Omniverse builds on this by providing its own version called omni.usd, specifically designed for development within the Omniverse environment.
While pxr and omni.usd are interchangeable, omni.usd is tailored for Omniverse. It serves to load and initialize pxr and Omniverse USD Resolver that supports opening USD from Omniverse/HTTP urls. You will likely need to use both pxr and omni.usd, depending on your specific goals and requirements.
Code examples
Move a prim
The following code snippets demonstrate how to load an existing stage, retrieve a primitive (prim), and move it. Since there are multiple methods to achieve this, examples are provided using both pxr and omni.usd. Additionally, there is a note on how to use omni.usd in headless mode.
Using pxr
from pxr import Usd, Gf # Open stage stage = Usd.Stage.Open('./path/test.usd') # Get the prim in prim_path prim_path = "/World/My_prim" my_prim = stage.GetPrimAtPath(prim_path) if not my_prim.IsValid(): print(f"The prim at '{prim_path}' does not exist.") else: print(f"The prim at '{prim_path}' exist.") # Get its position as a GfVec3d prim_pos = my_prim.GetAttribute("xformOp:translate").Get(0) # # Define new position step = Gf.Vec3d(1, 1, 1) new_pos = prim_pos + step # # Set its position to the new position success = my_prim.GetAttribute("xformOp:translate").Set(new_pos, 0)
Notice that when reading/writing the position of my_prim, we are getting the Attribute “translate” of a xformOp under my_prim. “.Get(0)” and “.Set(0)” refer to the fact that an XForm may contain several “translate” operations, and we are getting/setting only the first one.
Using omni.usd
The following code assumes the stage was previously opened in Omniverse.
import omni.usd from pxr import Gf, UsdGeom # Get default context context = omni.usd.get_context() # Open current stage stage = context.get_stage() # List all prims to confirm structure for prim in stage.Traverse(): print(prim.GetPath()) # Get the prim in prim_path prim_path = "/World/My_prim" my_prim = stage.GetPrimAtPath(prim_path) if my_prim.IsValid(): print("The prim is valid and it was found") else: print("The prim is not valid or not exist in the scene.") # Get its position as a GfVec3d prim_pos = my_prim.GetAttribute("xformOp:translate").Get(0) # # Define new position step = Gf.Vec3d(0.5, 0.5, 0.5) new_pos = prim_pos + step # # Set its position to the new position success = my_prim.GetAttribute("xformOp:translate").Set(new_pos, 0)
omni.usd also has a set_prop_val function that could be used for this purpose.
Using omni.usd in headless mode
If the stage wasn’t previously loaded (for example, when working in headless mode), it is necessary to first launch the Omniverse Toolkit using a helper class called SimulationApp.
from omni.isaac.kit import SimulationApp from omni.isaac.core.utils.stage import open_stage # Launch Omniverse Toolkit - choosing to run headless simulation_app = SimulationApp({"headless": True}) # Load stage stage_path = "C:\Users\ov-user\test.usd" open_stage(stage_path) # Get prim in prim path from omni.isaac.core.utils import prims prim_path = "World/my_prim" my_prim = prims.get_prim_at_path(prim_path) # Do something # ... # Close SimulationApp simulation_app.close()
For similar blogs please visit Marvik Blogs.