TimeCodes and TimeSamples#
This lesson, TimeCode and TimeSample, shows us how to set up animation in a stage using OpenUSD.
In this lesson, we will:
Set start and end timeCodes for a stage. Learn how to set start and end timeCode metadata for a USD stage, establishing a timeline that forms the foundation for animated scenes.
Set timeSamples on attributes. Gain the skills to set timeSamples on individual attributes, allowing us to animate specific properties of prims over time.
What are TimeCodes and TimeSamples?#
In OpenUSD, timeCode and timeSample are two important concepts that enable us to work with animations and simulation in USD scenes.
TimeCode is a point in time with no unit assigned to it. You can think of these as frames whose units are derived from the stage.
TimeSample refers to the individual time-varying values associated with an attribute in USD. Each attribute can have a collection of timeSamples that map timeCode to the attribute’s data type values, allowing for animation over time. For a reminder of the purpose of attributes, please review the introductory lesson on attributes.
How Does It Work?#
In a USD scene, the timeCode ordinates of all timeSamples are scaled to
seconds based on the timeCodesPerSecond
metadata value defined in the root
layer.
This allows flexibility in encoding timeSamples within a range and scale suitable for the application, while maintaining a robust mapping to real-world time for playback and decoding.
For example, if the root layer has timeCodesPerSecond=24
, a timeCode value
of 48.0
would correspond to 2 seconds (48/24) of real time after the
timeCode 0
.
TimeSamples are used to store time-varying data for attributes, such as positions, rotations, or material properties. When an attribute is evaluated at a specific timeCode, the value is linearly interpolated from the surrounding timeSamples, allowing for smooth animation playback.
Working With Python#
Below is an example of how we can get or set timeSamples in Python. First,
we’re getting the timeSamples of the displayColor
on a cube prim. This
method returns a vector of timeCode ordinates at which time samples are
authored for the given attribute.
Lastly, we’re setting a translation value of a sphere at a specified timeCode. This method sets the timeSample value of the attribute at the specified timeCode.
1# Returns authored TimeSamples
2cube.GetDisplayColorAttr().GetTimeSamples()
3
4# Sets TimeSample Value (Gf.Vec3d(0,-4.5,0)) at a specified TimeCode (30)
5sphere_xform_api.SetTranslate(Gf.Vec3d(0,-4.5,0), time=Usd.TimeCode(30))
Examples#
Let’s create a USD stage to serve as the starting point for the example in this lesson. We will create a simple stage with a sphere and a blue cube as a backdrop.
1# Import the necessary modules from the `pxr` library:
2from pxr import Usd, UsdGeom, Gf
3
4# Create a new USD stage file named "timecode_sample.usda":
5stage: Usd.Stage = create_new_stage("_assets/timecode_sample.usda")
6
7# Define a transform ("Xform") primitive at the "/World" path:
8world: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
9
10# Define a Sphere primitive as a child of the transform at "/World/Sphere" path:
11sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))
12
13# Define a blue Cube as a background prim:
14box: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Backdrop"))
15box.GetDisplayColorAttr().Set([(0.0, 0.0, 1.0)])
16cube_xform_api = UsdGeom.XformCommonAPI(box)
17cube_xform_api.SetScale(Gf.Vec3f(5, 5, 0.1))
18cube_xform_api.SetTranslate(Gf.Vec3d(0, 0, -2))
19
20# Save the stage to the file:
21stage.Save()
Example 1: Setting Start and End TimeCodes#
TimeCode
specifies an exact frame or moment in the animation timeline. It allows for precise control over the timing of changes to properties, enabling smooth and accurate animation of 3D objects.
A Usd.TimeCode
is therefore a unitless, generic time measurement that serves as the ordinate for time-sampled data in USD files. Usd.Stage
defines the mapping of TimeCode
s to units like seconds and frames.
To set the stage’s start
TimeCode and end
TimeCode metadata, use the SetStartTimeCode()
and SetEndTimeCode()
methods.
1from pxr import Usd, UsdGeom, Gf
2
3stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_sample.usda")
4world: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
5sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))
6box: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Backdrop"))
7box.GetDisplayColorAttr().Set([(0.0, 0.0, 1.0)])
8cube_xform_api = UsdGeom.XformCommonAPI(box)
9cube_xform_api.SetScale(Gf.Vec3f(5, 5, 0.1))
10cube_xform_api.SetTranslate(Gf.Vec3d(0, 0, -2))
11
12# Set the `start` and `end` timecodes for the stage:
13stage.SetStartTimeCode(1)
14stage.SetEndTimeCode(60)
15
16# Export to a new flattened layer for this example.
17stage.Export("_assets/timecode_ex1.usda", addSourceFileComment=False)
Note the stage metadata at the top of the layer.
Example 2: Setting TimeSamples for Attributes#
TimeSamples represent a collection of attribute values at various points in time, allowing OpenUSD to interpolate between these values for animation purposes.
When animating an attribute, you define a timeCode at which the value should be applied. These values are then interpolated between the timeSamples to get the value that should be applied at the current timeCode.
To assign a value at a particular timeCode, use the Set()
method.
Set()
takes two arguments: the timeCode and the value to assign.
For example, if you want to set the size of a cube to 1
at timeCode 1
and 10
at timeCode 60
:
1# Get the size attribute of the cube
2cube_size_attr: Usd.Attribute = cube_prim.GetSizeAttr()
3# Set the size of the cube at time=1 to 1
4cube_size_attr.Set(time=1, value=1)
5# Set the size of the cube at time=60 to 10
6cube_size_attr.Set(time=60, value=10)
USD will interpolate the values for the cube’s size attribute between set timeSamples.
Let’s create a sphere that moves up and down using the XformCommonAPI
.
1from pxr import Usd, UsdGeom
2
3stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_sample.usda")
4world: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
5sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))
6box: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Backdrop"))
7box.GetDisplayColorAttr().Set([(0.0, 0.0, 1.0)])
8cube_xform_api = UsdGeom.XformCommonAPI(box)
9cube_xform_api.SetScale(Gf.Vec3f(5, 5, 0.1))
10cube_xform_api.SetTranslate(Gf.Vec3d(0, 0, -2))
11
12stage.SetStartTimeCode(1)
13stage.SetEndTimeCode(60)
14
15# Grab the translate
16if translate_attr := sphere.GetTranslateOp().GetAttr():
17 translate_attr.Clear()
18
19# Create XformCommonAPI object for the sphere
20sphere_xform_api = UsdGeom.XformCommonAPI(sphere)
21
22# Set translation of the sphere at time 1
23sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=1)
24# Set translation of the sphere at time 30
25sphere_xform_api.SetTranslate(Gf.Vec3d(0, -4.50, 0), time=30)
26# Set translation of the sphere at time 45
27sphere_xform_api.SetTranslate(Gf.Vec3d(0, -5.00, 0), time=45)
28# Set translation of the sphere at time 50
29sphere_xform_api.SetTranslate(Gf.Vec3d(0, -3.25, 0), time=50)
30# Set translation of the sphere at time 60
31sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=60)
32
33# Export to a new flattened layer for this example.
34stage.Export("_assets/timecode_ex2a.usda", addSourceFileComment=False)
TimeSamples can be used for baked, per-frame animation and it is good for interchange that is reproducible. However, timeSamples are not a replacement for animation curves.
For more complex animation it is not recommended to define the animation using scripting but rather in other Digital Content Creation (DCC) Applications.
It is possible to set timeSamples for different attributes. We can demonstrate this with the scale of the sphere.
1from pxr import Usd, UsdGeom
2
3stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_sample.usda")
4world: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
5sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))
6box: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Backdrop"))
7box.GetDisplayColorAttr().Set([(0.0, 0.0, 1.0)])
8cube_xform_api = UsdGeom.XformCommonAPI(box)
9cube_xform_api.SetScale(Gf.Vec3f(5, 5, 0.1))
10cube_xform_api.SetTranslate(Gf.Vec3d(0, 0, -2))
11
12stage.SetStartTimeCode(1)
13stage.SetEndTimeCode(60)
14
15if translate_attr := sphere.GetTranslateOp().GetAttr():
16 translate_attr.Clear()
17if scale_attr := sphere.GetScaleOp().GetAttr():
18 scale_attr.Clear()
19
20sphere_xform_api = UsdGeom.XformCommonAPI(sphere)
21
22sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=1)
23sphere_xform_api.SetTranslate(Gf.Vec3d(0, -4.50, 0), time=30)
24sphere_xform_api.SetTranslate(Gf.Vec3d(0, -5.00, 0), time=45)
25sphere_xform_api.SetTranslate(Gf.Vec3d(0, -3.25, 0), time=50)
26sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=60)
27
28# Set scale of the sphere at time 1
29sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=1)
30# Set scale of the sphere at time 30
31sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=30)
32# Set scale of the sphere at time 45
33sphere_xform_api.SetScale(Gf.Vec3f(1.00, 0.20, 1.25), time=45)
34# Set scale of the sphere at time 50
35sphere_xform_api.SetScale(Gf.Vec3f(0.75, 2.00, 0.75), time=50)
36# Set scale of the sphere at time 60
37sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=60)
38
39# Export to a new flattened layer for this example.
40stage.Export("_assets/timecode_ex2b.usda", addSourceFileComment=False)
Key Takeaways#
To sum it up, timeCode provides a unitless time ordinate scaled to real-world time, while timeSample stores the actual attribute values at specific timeCode ordinates. Understanding these concepts unlocks a way for creating, manipulating, and rendering dynamic scenes and simulations in OpenUSD-based workflows across various industries.