Writing Transforms#
Transforms are ordinary runtime stage attributes with a transform semantic.
The canonical transform attribute is omni:xform. The legacy
omni:fabric:localMatrix name is also accepted, but new code should prefer
omni:xform.
ovrtx uses the USD row-vector matrix convention: translation is stored in the
last row of a 4x4 matrix, at matrix[3][0..2] or flat indices 12..14.
4x4 Matrix Writes#
Python uses NumPy-style tensor shapes. A batch of N 4x4 transforms has
shape (N, 4, 4) and dtype float64.
# Back a lane-16 per-prim transform with a 3-D ndarray of shape=(N, 4, 4).
# Translate /World/Camera to (10, 20, 30) using USD row-vector convention
# (translation lives in matrix[3][0..2]).
xform = np.eye(4, dtype=np.float64)
xform[3, 0:3] = [10.0, 20.0, 30.0]
transforms = xform.reshape(1, 4, 4) # shape=(1, 4, 4)
with ovstage.PathDictionary(stage) as paths:
path_list = paths.create_path_list_from_strings(["/World/Camera"])
with stage.query_from_path_list(path_list) as query:
attribute = paths.intern_token("omni:xform")
matrix_dtype = ovstage.numpy_to_dldatatype(transforms.dtype, lanes=16)
matrix_tensor = ovstage.make_dltensor(transforms, dtype=matrix_dtype, shape=[1], ndim=1)
stage.write_attribute(
query,
attribute,
ordinal=2,
tensors=matrix_tensor,
is_array=False,
semantic=ovstage.AttributeSemantic.MATRIX,
).wait()
stage.advance_write_floor(2, ovstage.Scope.ALL).wait()
with stage.read_attributes(query, [attribute], ovstage.OrdinalRange.latest(2)) as read:
group = read.fetch_next()
values = np.from_dlpack(group.dlpack(0)).copy().reshape(1, 4, 4)
stage.release_group(group)
assert values.shape == (1, 4, 4)
paths.destroy_path_list(path_list)
C convenience helpers in <ovrtx/ovrtx_attributes.h> hide the
DLTensor descriptor boilerplate for common transform layouts.
ovx_string_t prim = ovx_str("/World/Plane");
ovrtx_xform_matrix44d_t matrix{};
matrix.v[0] = 1.0;
matrix.v[5] = 1.0;
matrix.v[10] = 1.0;
matrix.v[15] = 1.0;
matrix.v[12] = 5.0;
ovrtx_enqueue_result_t eq = ovrtx_set_xform_mat(renderer_, &prim, 1, &matrix);
ASSERT_API_SUCCESS(eq.status);
docs_wait_no_errors(renderer_, eq.op_index);
C Transform Helpers#
The C API also provides compact helpers for position/rotation/scale layouts and
for authoring omni:resetXformStack.
ovx_string_t prim = ovx_str("/World/Plane");
ovrtx_xform_pos3d_rot4f_scale3f_t transform{};
transform.position[0] = 6.0;
transform.rot_quat_xyzw[0] = 1.0f; // 180 degrees around X.
transform.scale[0] = 2.0f;
transform.scale[1] = 3.0f;
transform.scale[2] = 4.0f;
ovrtx_enqueue_result_t eq = ovrtx_set_xform_pos_rot_scale(renderer_, &prim, 1, &transform);
ASSERT_API_SUCCESS(eq.status);
docs_wait_no_errors(renderer_, eq.op_index);
ovx_string_t prim = ovx_str("/World/Plane");
ovrtx_xform_pos3d_rot3x3f_t transform{};
transform.position[0] = 7.0;
transform.rot_matrix[0] = -1.0f;
transform.rot_matrix[4] = -1.0f;
transform.rot_matrix[8] = 1.0f;
ovrtx_enqueue_result_t eq = ovrtx_set_xform_pos_rot3x3(renderer_, &prim, 1, &transform);
ASSERT_API_SUCCESS(eq.status);
docs_wait_no_errors(renderer_, eq.op_index);
ovx_string_t prim = ovx_str("/World/Plane");
bool reset_stack = true;
ovrtx_enqueue_result_t eq = ovrtx_set_reset_xform_stack(renderer_, &prim, 1, &reset_stack);
ASSERT_API_SUCCESS(eq.status);
docs_wait_no_errors(renderer_, eq.op_index);
Repeated Updates#
For per-frame transform animation, avoid rebuilding descriptors on every update:
Use Attribute Bindings when the application already owns the transform tensors and copying into ovrtx is acceptable.
Use Attribute Mapping when CUDA or CPU code should write directly into ovrtx-owned attribute buffers.
Notes#
Transform matrices are
float64.Semantics are write-side conversion hints. Attribute reads use the raw storage layout.
In C, a 4x4 matrix attribute is represented as
shape=[N]withDLDataType{kDLFloat, 64, 16}.In Python, a 4x4 matrix attribute is represented as
shape=(N, 4, 4).