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.

# A per-prim 4x4 transform is a 3-D ndarray with 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)
renderer.write_attribute(
    prim_paths=["/World/Camera"],
    attribute_name="omni:xform",
    tensor=transforms,
    semantic=ovrtx.Semantic.XFORM_MAT4x4,
)
tensor = renderer.read_attribute(
    attribute_name="omni:xform",
    prim_paths=["/World/Camera"],
)
values = np.from_dlpack(tensor).reshape(1, 4, 4)
assert values.shape == (1, 4, 4)

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] with DLDataType{kDLFloat, 64, 16}.

  • In Python, a 4x4 matrix attribute is represented as shape=(N, 4, 4).