Metadata
Metadata is the smallest building block in Usd. It is part of the base class from which prims and properties inherit from and possesses a slightly different feature set than other parts of Usd.
Table of Contents
- API Overview In-A-Nutshell
- What should I use it for?
- Resources
- Overview
- Composition/Value resolution
- Working with metadata in your stages
- Basics (High level API)
- Validation of dict content
- Nested key path syntax
- Creating custom metadata fields via plugins
- Reading metadata documentation strings (High level API)
- Authored vs fallback metadata values (High level API)
- Reading/writing metadata via prim/property specs(Low level API)
- Special metadata fields for prims
- Special metadata fields for properties
- Special metadata fields for layers and stages
TL;DR - Metadata In-A-Nutshell
- Metadata attaches additional non-animatable data to prims/properties/layers via a dictionary
- Composition arcs and core data (specifiers/type names) is added via metadata
assetInfo
andcustomData
are predefined keys for prim metadata you can track asset/custom data with- To write to other keys, they must be registered via schemas.
What should I use it for?
In production, you'll use the assetInfo
/customData
prim metadata fields to track any production related data.
You can also use metadata to edit composition arcs, though the high level API offers nice class wrappers that wrap this for you.
from pxr import Sdf, Usd
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetMetadata("assetInfo", {"version": "1"})
prim.SetAssetInfoByKey("identifier", Sdf.AssetPath("bicycler.usd"))
prim.SetMetadata("customData", {"sizeUnit": "meter"})
prim.SetCustomDataByKey("nested:shape", "round")
Resources
Overview
Here is the class structure for the different API levels:
High Level API
flowchart TD usdObject(["Usd.Object (Includes Metadata API)"]) --> usdPrim([Usd.Prim]) usdObject --> usdProperty([Usd.Property]) usdProperty --> usdAttribute([Usd.Attribute]) usdProperty --> usdRelationship([Usd.Relationship]) usdStage(["Usd.Stage (Includes Metadata API)"])
Low Level API
flowchart TD sdfSpec(["Sdf.Spec (Includes Metadata API)"]) --> sdfPropertySpec([Sdf.Property]) sdfSpec --> sdfPrimSpec([Sdf.PrimSpec]) sdfSpec --> sdfVariantSetSpec([Sdf.VariantSetSpec]) sdfSpec --> sdfVariantSpec([Sdf.VariantSpec]) sdfPropertySpec --> sdfAttributeSpec([Sdf.AttributeSpec]) sdfPropertySpec --> sdfRelationshipSpec([Sdf.RelationshipSpec]) sdfLayer(["Sdf.Layer (Includes Metadata API)"])
Metadata is different in that it:
- Is the smallest building block in Usd (There are no subclasses) and its data is stored as a dictionary.
- Is extremely fast to access
- Can't be time varying:
- Composition arcs are written into metadata fields on prims, so you can't animate composition.
- Metadata stored in value clip files is ignored
- Is strongly typed via schemas, so you need to register a custom schema if you want custom keys. This way we can ensure fallback values/documentation per key/value and avoid random data flying through your pipelines. For example all your mesh attributes have metadata for exactly what type/role they must match.
- There are two special metadata keys for prims:
assetInfo
: Here you should dump asset related data. This is just a predefined standardized location all vendors/companies should adhere to when writing asset data that should be tracked.customData
: Here you can dump any data you want, a kind of scratch space, so you don't need to add you own schema. If you catch yourself misusing it too much, you should probably generate your own schema.
We go into more detail over in the schema section on how to create or lookup registered schemas.
Composition/Value resolution
Metadata is slightly different when it comes to value resolution. (As a reminder: value resolution
is just a fancy word for "what layer has the winning value out of all your layers where the data will be loaded from"):
- Nested dictionaries are combined
- Attribute metadata behaves by the same rules as attribute value resolution
- Core metadata (Metadata that affects composition/prim definitions):
- Composition metadata is composed via Listeditable-Ops. See our section here for more details. Be sure to understand these to save your self a lot of head-aches why composition works the way it does.
- Specific prim metadata has its own rule set (E.g. prim specifiers).
Working with metadata in your stages
Let's look at some actual code examples on how to modify metadata.
Basics (High level API)
"""
### General
# Has: 'HasAuthoredMetadata'/'HasAuthoredMetadataDictKey'/'HasMetadata'/'HasMetadataDictKey'
# Get: 'GetAllAuthoredMetadata'/'GetAllMetadata'/'GetMetadata'/'GetMetadataByDictKey'
# Set: 'SetMetadata'/'SetMetadataByDictKey',
# Clear: 'ClearMetadata'/'ClearMetadataByDictKey'
### Asset Info (Prims only)
# Has: 'HasAssetInfo'/'HasAssetInfoKey'/'HasAuthoredAssetInfo'/'HasAuthoredAssetInfoKey'
# Get: 'GetAssetInfo'/'GetAssetInfoByKey'
# Set: 'SetAssetInfo'/'SetAssetInfoByKey',
# Clear: 'ClearAssetInfo'/'ClearAssetInfoByKey'
### Custom Data (Prims, Properties(Attributes/Relationships), Layers)
# Has: 'HasCustomData'/'HasCustomDataKey'/'HasAuthoredCustomData'/'HasAuthoredCustomDataKey'
# Get: 'GetCustomData'/'GetCustomDataByKey'
# Set: 'SetCustomData'/'SetCustomDataByKey',
# Clear: 'ClearCustomData'/'ClearCustomDataByKey'
"""
from pxr import Usd, Sdf
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetAssetInfoByKey("identifier", Sdf.AssetPath("bicycler.usd"))
prim.SetAssetInfoByKey("nested", {"assetPath": Sdf.AssetPath("bicycler.usd"), "version": "1"})
prim.SetMetadataByDictKey("assetInfo", "nested:color", "blue")
attr = prim.CreateAttribute("tire:size", Sdf.ValueTypeNames.Float)
attr.SetMetadata("customData", {"sizeUnit": "meter"})
attr.SetCustomDataByKey("nested:shape", "round")
print(prim.HasAuthoredMetadata("assetInfo")) # Returns: True
print(prim.HasAuthoredMetadataDictKey("assetInfo", "identifier")) # Returns: True
print(prim.HasMetadata("assetInfo")) # Returns: True
print(prim.HasMetadataDictKey("assetInfo", "nested:color")) # Returns: True
# prim.ClearMetadata("assetInfo") # Remove all assetInfo in the current layer.
Validation of dict content
To create a valid metadata compatible dict, you can validate it:
data = {"myCustomKey": 1}
success_state, metadata, error_message = Sdf.ConvertToValidMetadataDictionary(data)
Nested key path syntax
To access nested dict keys, we use the :
symbol as the path separator.
from pxr import Usd, Sdf
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetAssetInfoByKey("nested:color", "blue")
print(prim.GetAssetInfo()) # Returns: {'nested': {'color': 'blue'}}
print(prim.GetAssetInfoByKey("nested:color")) # Returns: "blue"
The Get
/Set
methods without the ByKey
/ByDictKey
allow you to set root dict keys, e.g. SetMetadata("typeName", "Xform")
The ByKey
/ByDictKey
take a root key and a key path (with :
if nested), e.g. SetMetadataByDictKey('assetInfo', "data:version", 1)
which will result in {"assetInfo": {"data": "version": 1}}
Creating custom metadata fields via plugins
We can easily extend metadata fields via plugins. We cover this in detail in out metadata plugin section.
Reading metadata documentation strings (High level API)
This is quite useful if you need to expose docs in UIs.
from pxr import Usd, Sdf
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Cube")
# Shortcut to get the docs metadata
# Has: 'HasAuthoredDocumentation'
# Get: 'GetDocumentation'
# Set: 'SetDocumentation'
# Clear: 'ClearDocumentation'
print(prim.GetDocumentation())
for attr in prim.GetAttributes():
print(attr.GetName(), attr.GetDocumentation())
# Or
# print(attr.GetMetadata("documentation"))
Click here to view the result
"""
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.
doubleSided Although some renderers treat all parametric or polygonal
surfaces as if they were effectively laminae with outward-facing
normals on both sides, some renderers derive significant optimizations
by considering these surfaces to have only a single outward side,
typically determined by control-point winding order and/or
orientation. By doing so they can perform "backface culling" to
avoid drawing the many polygons of most closed surfaces that face away
from the viewer.
However, it is often advantageous to model thin objects such as paper
and cloth as single, open surfaces that must be viewable from both
sides, always. Setting a gprim's doubleSided attribute to
\c true instructs all renderers to disable optimizations such as
backface culling for the gprim, and attempt (not all renderers are able
to do so, but the USD reference GL renderer always will) to provide
forward-facing normals on each side of the surface for lighting
calculations.
extent Extent is re-defined on Cube only to provide a fallback value.
\sa UsdGeomGprim::GetExtentAttr().
orientation Orientation specifies whether the gprim's surface normal
should be computed using the right hand rule, or the left hand rule.
Please see for a deeper explanation and
generalization of orientation to composed scenes with transformation
hierarchies.
primvars:displayColor It is useful to have an "official" colorSet that can be used
as a display or modeling color, even in the absence of any specified
shader for a gprim. DisplayColor serves this role; because it is a
UsdGeomPrimvar, it can also be used as a gprim override for any shader
that consumes a displayColor parameter.
primvars:displayOpacity Companion to displayColor that specifies opacity, broken
out as an independent attribute rather than an rgba color, both so that
each can be independently overridden, and because shaders rarely consume
rgba parameters.
purpose Purpose is a classification of geometry into categories that
can each be independently included or excluded from traversals of prims
on a stage, such as rendering or bounding-box computation traversals.
See for more detail about how
purpose is computed and used.
size Indicates the length of each edge of the cube. If you
author size you must also author extent.
visibility Visibility is meant to be the simplest form of "pruning"
visibility that is supported by most DCC apps. Visibility is
animatable, allowing a sub-tree of geometry to be present for some
segment of a shot, and absent from others; unlike the action of
deactivating geometry prims, invisible geometry is still
available for inspection, for positioning, for defining volumes, etc.
xformOpOrder Encodes the sequence of transformation operations in the
order in which they should be pushed onto a transform stack while
visiting a UsdStage's prims in a graph traversal that will effect
the desired positioning for this prim and its descendant prims.
You should rarely, if ever, need to manipulate this attribute directly.
It is managed by the AddXformOp(), SetResetXformStack(), and
SetXformOpOrder(), and consulted by GetOrderedXformOps() and
GetLocalTransformation().
"""
Authored vs fallback metadata values (High level API)
The getters offer the distinction between retrieving authored or fallback values provided by the schemas that registered the metadata.
from pxr import Usd, Sdf
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetAssetInfoByKey("identifier", "bicycle.usd")
# The difference between "authored" and non "authored" methods is
# that "authored" methods don't return fallback values that come from schemas.
print(prim.GetAllAuthoredMetadata())
# Returns:
# {'assetInfo': {'identifier': 'bicycle.usd'},
# 'specifier': Sdf.SpecifierDef,
# 'typeName': 'Xform'}
print(prim.GetAllMetadata())
# Returns:
#{'assetInfo': {'identifier': 'bicycle.usd'},
# 'documentation': 'Concrete prim schema for a transform, which implements Xformable ',
# 'specifier': Sdf.SpecifierDef,
# 'typeName': 'Xform'}
Reading/writing metadata via prim/property specs(Low level API)
Same as with the layer customData, the lower level APIs on prim/property specs expose it to Python via lower camel case syntax in combination with direct assignment, instead of offer getter and setters.
from pxr import Sdf
layer = Sdf.Layer.CreateAnonymous()
### Prims ###
prim_spec = Sdf.CreatePrimInLayer(layer, "/bicycle")
prim_spec.specifier = Sdf.SpecifierDef
# Asset Info and Custom Data
prim_spec.assetInfo = {"identifier": Sdf.AssetPath("bicycle.usd")}
prim_spec.customData = {"myCoolData": "myCoolValue"}
# General metadata
# Has: 'HasInfo'
# Get: 'ListInfoKeys', 'GetMetaDataInfoKeys', 'GetInfo', 'GetFallbackForInfo', 'GetMetaDataDisplayGroup'
# Set: 'SetInfo', 'SetInfoDictionaryValue'
# Clear: 'ClearInfo'
print(prim_spec.ListInfoKeys()) # Returns: ['assetInfo', 'customData', 'specifier']
# To get all registered schema keys run:
print(prim_spec.GetMetaDataInfoKeys())
"""Returns: ['payloadAssetDependencies', 'payload', 'kind', 'suffix', 'inactiveIds', 'clipSets',
'HDAKeepEngineOpen', 'permission', 'displayGroupOrder', 'assetInfo', 'HDAParms', 'instanceable',
'symmetryFunction', 'HDATimeCacheMode', 'clips', 'HDAAssetName', 'active', 'HDATimeCacheEnd',
'customData', 'HDAOptions', 'prefix', 'apiSchemas', 'suffixSubstitutions', 'symmetryArguments',
'hidden', 'HDATimeCacheStart', 'sdrMetadata', 'typeName', 'HDATimeCacheInterval', 'documentation',
'prefixSubstitutions', 'symmetricPeer']"""
# For the fallback values and UI grouping hint you can use
# 'GetFallbackForInfo' and 'GetMetaDataDisplayGroup'.
# Prim spec core data is actually also just metadata info
prim_spec.SetInfo("specifier", Sdf.SpecifierDef)
prim_spec.SetInfo("typeName", "Xform")
# Is the same as:
prim_spec.specifier = Sdf.SpecifierDef
prim_spec.typeName = "Xform"
### Properties ###
attr_spec = Sdf.AttributeSpec(prim_spec, "tire:size", Sdf.ValueTypeNames.Float)
# Custom Data
attr_spec.customData = {"myCoolData": "myCoolValue"}
# We can actually use the attr_spec.customData assignment here too,
# doesn't make that much sense though
# General metadata
# Has: 'HasInfo'
# Get: 'ListInfoKeys', 'GetMetaDataInfoKeys', 'GetInfo', 'GetFallbackForInfo', 'GetMetaDataDisplayGroup'
# Set: 'SetInfo', 'SetInfoDictionaryValue'
# Clear: 'ClearInfo'
# The API here is the same as for the prim_spec, as it all inherits from Sdf.Spec
# To get all registered schema keys run:
print(attr_spec.GetMetaDataInfoKeys())
"""Returns: ['unauthoredValuesIndex', 'interpolation', 'displayGroup', 'faceIndexPrimvar',
'suffix', 'constraintTargetIdentifier', 'permission', 'assetInfo', 'symmetryFunction', 'uvPrimvar',
'elementSize', 'allowedTokens', 'customData', 'prefix', 'renderType', 'symmetryArguments',
'hidden', 'displayName', 'sdrMetadata', 'faceOffsetPrimvar', 'weight', 'documentation',
'colorSpace', 'symmetricPeer', 'connectability']
"""
Special metadata fields for prims
Here are the most common prim metadata keys we'll be working with.
Active/Activation
The active
metadata controls if the prim and its children are loaded or not.
We only cover here how to set the metadata, for more info checkout our Loading mechansims section. Since it is a metadata entry, it can not be animated. For animated pruning we must use visibility.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetActive(False)
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.active = False
# Or
prim_spec.SetInfo(prim_spec.ActiveKey, True)
Asset Info
The assetInfo
metadata carries asset related data. This is just a predefined standardized location all vendors/companies should adhere to when writing asset data that should be tracked.
There are currently four standardized keys:
identifier
(Sdf.AssetPath): The asset identifier (that the asset resolver can resolve)name
(str): The name of the asset.version
(str): The version of the asset.payloadAssetDependencies
(Sdf.AssetPathArray()): This is typically attached to the prim where you attach payloads to that when you unloaded payloads, you can still see what is in the file without traversing the actual layer content. It is up to you to manage the content of this list to be synced with the actual payload(s) content.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetMetadata("assetInfo", {"identifier": Sdf.AssetPath("bicycler.usd")})
prim.SetAssetInfoByKey("name", "bicycle")
prim.SetAssetInfoByKey("version", "v001")
prim.SetAssetInfoByKey("payloadAssetDependencies", Sdf.AssetPathArray(["assetIndentifierA", "assetIndentifierA"]))
# Sdf.AssetPathArray([]) auto-casts all elements to Sdf.AssetPath objects.
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.assetInfo = {"identifier": Sdf.AssetPath("bicycle.usd")}
prim_spec.assetInfo["name"] = "bicycle"
prim_spec.assetInfo["version"] = "v001"
prim_spec.assetInfo["payloadAssetDependencies"] = Sdf.AssetPathArray(["assetIndentifierA", "assetIndentifierA"])
# Sdf.AssetPathArray([]) auto-casts all elements to Sdf.AssetPath objects.
Custom Data
The customData
field can be for any data you want, a kind of scratch space, so you don't need to add you own schema. If you catch yourself misusing it too much, you should probably generate your own schema.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetMetadata("customData", {"sizeUnit": "meter"})
prim.SetCustomDataByKey("nested:shape", "round")
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.customData = {"myCoolData": "myCoolValue"}
Comments
There is also a special key to track user comments:
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetMetadata("comment", "This is a cool prim!")
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.SetInfo("comment", "This is a cool prim spec!")
Icon (UI)
You can also write an icon
key into the customData
dict, which UI applications can then optionally use to draw the prim icon with.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetMetadata("comment", "This is a cool prim!")
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.SetInfo("comment", "This is a cool prim spec!")
Hidden (UI)
There is also a special key hidden
key that is a UI hint that can be used by applications to hide the prim in views. It is up to the application to implement.
This also exists for properties, but is not read by most UIs in apps/DCCs.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Xform")
prim.SetHidden(True)
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/cube")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.SetInfo("hidden", True)
Special metadata fields for properties
Setting metadata works the same for properties, but they do have a different set of default core metadata. Here we cover the most important ones.
Support for animation (USD speak variability)
The term variability
in USD just means if a property can have animation (time samples) or not. There are two values:
Sdf.VariabilityUniform
(No animation/time samples)Sdf.VariabilityVarying
(Supports time samples)
The variability actually only declares the intent of time capabilities of the property. You can still write time samples for uniform variability attributes, there are chances though that something else somewhere in the API/Hydra won't work then though. So as a best practice don't write animated attributes to Sdf.VariabilityUniform
declared schema attributes. Relationships always have the Sdf.VariabilityUniform
intent as they can't be animated.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/box")
prim = stage.DefinePrim(prim_path, "Cube")
attr = prim.CreateAttribute("height", Sdf.ValueTypeNames.Double)
attr.SetMetadata("variability", Sdf.VariabilityUniform)
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/box")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.typeName = "Cube"
attr_spec = Sdf.AttributeSpec(prim_spec, "height", Sdf.ValueTypeNames.Double)
attr_spec.SetInfo("variability", Sdf.VariabilityVarying)
Custom vs schema defined properties
All properties that are not registered via a schema are marked as custom
.
This is one of the examples where we can clearly see the benefit of the high level API:
It automatically checks if a property is in the assigned schemas and marks it as custom
if necessary.
With the lower level API, we have to mark it ourselves.
from pxr import Sdf, Usd
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/box")
prim = stage.DefinePrim(prim_path, "Cube")
attr = prim.CreateAttribute("height", Sdf.ValueTypeNames.Double)
# This is not necessary to do explicitly as
# the high level API does this for us.
attr.SetMetadata("custom", True)
# Or
print(attr.IsCustom())
attr.SetCustom(True)
### Low Level ###
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/box")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.typeName = "Cube"
attr_spec = Sdf.AttributeSpec(prim_spec, "height", Sdf.ValueTypeNames.Double)
attr_spec.SetInfo("custom", True)
Special metadata fields for layers and stages
For stages (root layer/session layer) and layers, we can also write a few special fields as covered below.
Stage/Root Layer Default Render Settings (High/Low level API)
We can supply a default render settings prim path on our root layer. This will be used in DCCs as the default render settings to drive Hydra rendering.
### High Level ###
from pxr import Usd
stage = Usd.Stage.CreateInMemory()
stage.GetRootLayer().pseudoRoot.SetInfo(
"renderSettingsPrimPath", "/Render/rendersettings"
)
### Low Level ###
from pxr import Sdf
layer = Sdf.Layer.CreateAnonymous()
layer.pseudoRoot.SetInfo("renderSettingsPrimPath", "/Render/rendersettings")
For example in Houdini we can then see it marked with the "Default" prefix in our viewport display options.
Stage and layer metrics (FPS/Scene Unit Scale/Up Axis) (High/Low level API)
For more info about the FPS, see our animation section.
We can supply an up axis and scene scale hint in the layer metadata, but this does not seem to be used by most DCCs or in fact Hydra itself when rendering the geo. So if you have a mixed values, you'll have to counter correct via transforms yourself.
The default scene metersPerUnit
value is centimeters (0.01) and the default upAxis
is Y
.
See Scene Up Axis API Docs and Scene Unit API Docs for more info.
from pxr import Sdf, Usd, UsdGeom
### High Level ###
stage = Usd.Stage.CreateInMemory()
prim_path = Sdf.Path("/bicycle")
prim = stage.DefinePrim(prim_path, "Cone")
size_attr = prim.GetAttribute("radius")
for frame in range(1001, 1006):
time_code = Usd.TimeCode(frame)
size_attr.Set(frame - 1000, time_code)
# FPS Metadata
time_samples = Sdf.Layer.ListAllTimeSamples(layer)
stage.SetTimeCodesPerSecond(25)
stage.SetFramesPerSecond(25)
stage.SetStartTimeCode(time_samples[0])
stage.SetEndTimeCode(time_samples[-1])
# Scene Unit Scale
UsdGeom.SetStageMetersPerUnit(stage, UsdGeom.LinearUnits.centimeters)
# To map 24 fps (default) to 25 fps we have scale by 24/25 when loading the layer in the Sdf.LayerOffset
# Scene Up Axis
UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.y) # Or UsdGeom.Tokens.z
### Low Level ###
from pxr import Sdf
layer = Sdf.Layer.CreateAnonymous()
prim_path = Sdf.Path("/bicycle")
prim_spec = Sdf.CreatePrimInLayer(layer, prim_path)
prim_spec.specifier = Sdf.SpecifierDef
prim_spec.typeName = "Cube"
attr_spec = Sdf.AttributeSpec(prim_spec, "size", Sdf.ValueTypeNames.Double)
for frame in range(1001, 1006):
value = float(frame - 1000)
layer.SetTimeSample(attr_spec.path, frame, value)
# FPS Metadata
layer.timeCodesPerSecond = 25
layer.framesPerSecond = 25
layer.startTimeCode = time_samples[0]
layer.endTimeCode = time_samples[-1]
# Scene Unit Scale
layer.pseudoRoot.SetInfo(UsdGeom.Tokens.metersPerUnit, UsdGeom.LinearUnits.centimeters)
# Scene Up Axis
layer.pseudoRoot.SetInfo(
UsdGeom.Tokens.upAxis, UsdGeom.Tokens.y
) # Or UsdGeom.Tokens.z
Stage and layer customData metadata (High/Low level API)
This is often used to track pipeline relevant data in DCCs. For node based DCCs, this is a convenient way to pass general data down through the node network. For layer based DCCs, this can be used to tag layers (for example to anonymous layers that carry specific pipeline data).
Layer metadata, like some other classes in the low level API, uses the lower camel case syntax in combination with direct assignment, instead of offer getter and setters. Here we can just use the standard Python dict methods.
from pxr import Usd, Sdf
layer = Sdf.Layer.CreateAnonymous()
layer.customLayerData = {"myCustomPipelineKey": "myCoolValue"}
As we are operating on the layer (lower level API), we do not see any composed metadata and instead only the data in the layer we are looking at. The Usd.Stage
class also offers the metadata methods, it follows a different logic though:
It writes the metadata to the session or root layer. So you won't see any composed metadata of individual layers, only those of the session/root layer (depending on the edit target according to the docs
from pxr import Usd, Sdf
stage = Usd.Stage.CreateInMemory()
stage.SetMetadata("customLayerData", {"myCustomStageData": 1})
# Is the same as:
layer = stage.GetRootLayer()
metadata = layer.customLayerData
metadata["myCustomRootData"] = 1
layer.customLayerData = metadata
# Or:
layer = stage.GetSessionLayer()
metadata = layer.customLayerData
metadata["myCustomSessionData"] = 1
layer.customLayerData = metadata
# To get the value from the session/root layer depending on the edit target:
stage.GetMetadata("customLayerData")