Time Codes and Time Samples#
This lesson, Time Code and Time Sample, shows us how to set up animation in a stage using OpenUSD.
In this lesson, we will:
Set start and end time codes for a stage. Learn how to set start and end time code metadata for a USD stage, establishing a timeline that forms the foundation for animated scenes.
Set time samples on attributes. Gain the skills to set time samples on individual attributes, allowing us to animate specific properties of prims over time.
What are Time Codes and Time Samples?#
In OpenUSD, time code and time sample are two important concepts that enable us to work with animations and simulation in USD scenes.
Time code 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.
Time sample refers to the individual time-varying values associated with an attribute in USD. Each attribute can have a collection of time samples that map time code 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 time code ordinates of all time samples are scaled to
seconds based on the timeCodesPerSecond metadata value defined in the root layer.
This allows flexibility in encoding time samples 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 time code value
of 48.0 would correspond to 2 seconds (48/24) of real time after the
time code 0.
Time samples are used to store time-varying data for attributes, such as positions, rotations, or material properties. When an attribute is evaluated at a specific time code, the value is linearly interpolated from the surrounding time samples, allowing for smooth animation playback.
Working With Python#
Below is an example of how we can get or set time samples in Python. First,
we’re getting the time samples of the displayColor on a cube prim. This
method returns a vector of time code ordinates at which time samples are
authored for the given attribute.
Lastly, we’re setting a translation value of a sphere at a specified time code. This method sets the time sample value of the attribute at the specified time code.
1# Returns authored time samples
2cube.GetDisplayColorAttr().GetTimeSamples()
3
4# Sets time sample 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":
5file_path = "_assets/timecode_sample.usda"
6stage: Usd.Stage = Usd.Stage.CreateNew(file_path)
7
8# Define a transform ("Xform") primitive at the "/World" path:
9world: UsdGeom.Xform = UsdGeom.Xform.Define(stage, "/World")
10
11# Define a Sphere primitive as a child of the transform at "/World/Sphere" path:
12sphere: UsdGeom.Sphere = UsdGeom.Sphere.Define(stage, world.GetPath().AppendPath("Sphere"))
13
14# Define a blue Cube as a background prim:
15box: UsdGeom.Cube = UsdGeom.Cube.Define(stage, world.GetPath().AppendPath("Backdrop"))
16box.GetDisplayColorAttr().Set([(0.0, 0.0, 1.0)])
17cube_xform_api = UsdGeom.XformCommonAPI(box)
18cube_xform_api.SetScale(Gf.Vec3f(5, 5, 0.1))
19cube_xform_api.SetTranslate(Gf.Vec3d(0, 0, -2))
20
21# Save the stage to the file:
22stage.Save()
Example 1: Setting Start and End Time Codes#
Time code 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 time codes to units like seconds and frames.
To set the stage’s startTimeCode and endTimeCode metadata, use the SetStartTimeCode() and SetEndTimeCode() methods.
1from pxr import Usd
2
3# Open stage from starting point usda
4stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_sample.usda")
5
6# Set the `start` and `end` time codes for the stage:
7stage.SetStartTimeCode(1)
8stage.SetEndTimeCode(60)
9
10# Export to a new flattened layer for this example.
11stage.Export("_assets/timecode_ex1.usda", addSourceFileComment=False)
Note the stage metadata at the top of the layer.
Example 2: Setting Time Samples for Attributes#
Time samples 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 time code at which the value should be applied. These values are then interpolated between the time samples to get the value that should be applied at the current time code.
To assign a value at a particular time code, use the Set() method.
Set() takes two arguments: the time code and the value to assign.
For example, if you want to set the size of a cube to 1 at time code 1 and 10 at time code 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 time samples.
Let’s create a sphere that moves up and down using the XformCommonAPI.
1from pxr import Usd, UsdGeom, Gf
2
3# Open stage from example 1
4stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_ex1.usda")
5
6sphere: UsdGeom.Sphere = UsdGeom.Sphere.Get(stage, "/World/Sphere")
7
8# Clear any existing translation
9if translate_attr := sphere.GetTranslateOp().GetAttr():
10 translate_attr.Clear()
11
12# Create XformCommonAPI object for the sphere
13sphere_xform_api = UsdGeom.XformCommonAPI(sphere)
14
15# Set translation of the sphere at time 1
16sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=1)
17# Set translation of the sphere at time 30
18sphere_xform_api.SetTranslate(Gf.Vec3d(0, -4.50, 0), time=30)
19# Set translation of the sphere at time 45
20sphere_xform_api.SetTranslate(Gf.Vec3d(0, -5.00, 0), time=45)
21# Set translation of the sphere at time 50
22sphere_xform_api.SetTranslate(Gf.Vec3d(0, -3.25, 0), time=50)
23# Set translation of the sphere at time 60
24sphere_xform_api.SetTranslate(Gf.Vec3d(0, 5.50, 0), time=60)
25
26# Export to a new flattened layer for this example.
27stage.Export("_assets/timecode_ex2a.usda", addSourceFileComment=False)
Time samples can be used for baked, per-frame animation and it is good for interchange that is reproducible. However, time samples 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 time samples for different attributes. We can demonstrate this with the scale of the sphere.
1from pxr import Usd, UsdGeom, Gf
2
3# Open stage from example 2a
4stage: Usd.Stage = Usd.Stage.Open("_assets/timecode_ex2a.usda")
5
6sphere: UsdGeom.Sphere = UsdGeom.Sphere.Get(stage, "/World/Sphere")
7
8if scale_attr := sphere.GetScaleOp().GetAttr():
9 scale_attr.Clear()
10
11sphere_xform_api = UsdGeom.XformCommonAPI(sphere)
12
13# Set scale of the sphere at time 1
14sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=1)
15# Set scale of the sphere at time 30
16sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=30)
17# Set scale of the sphere at time 45
18sphere_xform_api.SetScale(Gf.Vec3f(1.00, 0.20, 1.25), time=45)
19# Set scale of the sphere at time 50
20sphere_xform_api.SetScale(Gf.Vec3f(0.75, 2.00, 0.75), time=50)
21# Set scale of the sphere at time 60
22sphere_xform_api.SetScale(Gf.Vec3f(1.00, 1.00, 1.00), time=60)
23
24# Export to a new flattened layer for this example.
25stage.Export("_assets/timecode_ex2b.usda", addSourceFileComment=False)
Key Takeaways#
To sum it up, time code provides a unitless time ordinate scaled to real-world time, while time sample stores the actual attribute values at specific time code ordinates. Understanding these concepts unlocks a way for creating, manipulating, and rendering dynamic scenes and simulations in OpenUSD-based workflows across various industries.