Extension: omni.kit.scene_view.xr_utils-1.0.2

Documentation Generated: Jul 18, 2026

Spatial Sources#

A SpatialSource is one transform contribution in the placement of a UiContainer. Each source produces a single omni.ui.scene.Transform node; a list of sources — passed as the space_stack argument to UiContainer — produces a chain of transforms applied left-to-right (outer-to-inner).

container = UiContainer(
    scene_view_type=XRSceneView,
    initial_component=panel_component,
    space_stack=[
        SpatialSource.new_translation_source(Gf.Vec3f(0.0, 170.0, -60.0)),
        SpatialSource.new_look_at_camera_source(),
    ],
)

The list above first translates to a world position, then rotates the panel to face the camera around that pivot — the classic billboard HUD pattern. Re-order the list, and the camera rotation would happen first and the translation would be interpreted in the rotated frame.

You do not subclass SpatialSource directly in normal use; instead, you call one of the static factory methods listed below. Each returns a fully-built SpatialSource you can hand to a UiContainer.

new_translation_source(position)#

Apply a fixed translation in stage units (typically centimetres).

SpatialSource.new_translation_source(Gf.Vec3f(0.0, 150.0, -80.0))
# Move 1.5 m up and 80 cm in front of whatever pivot precedes it.

The translation combines with sibling sources; if it is the first source in the stack, it is interpreted in world space.

new_rotation_source(euler)#

Apply a fixed Euler-angle rotation. The vector is passed straight through to omni.ui.scene.Matrix44.get_rotation_matrix, which interprets the components as rotations around X, Y, and Z in degrees by default.

SpatialSource.new_rotation_source(Gf.Vec3f(0.0, 45.0, 0.0))  # yaw 45°

For runtime-driven rotation, drive the manipulator directly (see container.manipulator.rotation_degrees) or feed a matrix through new_transform_matrix_source below.

new_scale_source(scale)#

Apply a per-axis scale factor. Pass equal values on all three axes for a uniform scale, or differing values to stretch the panel non-uniformly:

SpatialSource.new_scale_source(Gf.Vec3f(1.5, 1.5, 1.5))  # 1.5× uniform
SpatialSource.new_scale_source(Gf.Vec3f(2.0, 1.0, 1.0))  # wide aspect

Note that scaling a panel does not improve its rendered fidelity — the underlying texture resolution is set by WidgetComponent and stays fixed under scaling. Adjust resolution_scale on the component instead if a scaled panel looks soft.

new_look_at_camera_source()#

Rotate the panel to face the view camera every frame. In XR this tracks the headset’s view. The source contributes rotation only, leaving translation inherited from preceding sources — so place a translation or prim-path source before this one to position the pivot in world space.

space_stack = [
    SpatialSource.new_translation_source(Gf.Vec3f(0.0, 170.0, -60.0)),
    SpatialSource.new_look_at_camera_source(),
]

new_prim_path_source(prim_path)#

Anchor the UI to a USD prim’s world transform. The source samples the prim’s local-to-world transform every frame, so the UI tracks the prim’s motion in the stage.

SpatialSource.new_prim_path_source("/World/Robot/Head")

This source sets the full world transform of its Transform node, which means it overrides any translation, rotation, or scale that came before it in the stack. Stack additional offsets after it to apply relative motion:

space_stack = [
    SpatialSource.new_prim_path_source("/World/Robot/Head"),   # anchor
    SpatialSource.new_translation_source(Gf.Vec3f(0.0, 30.0, 0.0)),  # lift 30 cm
    SpatialSource.new_look_at_camera_source(),                 # face viewer
]

The path can be a Python str or an Sdf.Path. If the prim does not exist when the container is built, the transform will track it as soon as the prim appears — but the source itself does not poll; it relies on the underlying omni.kit.scene_view.xr.XRPrimPathTransformBasis to update.

new_transform_matrix_source(matrix)#

The escape hatch. Pass any USD Gf.Matrix4f or Gf.Matrix4d; it is applied verbatim. Useful when you have an existing transform from elsewhere in the application and want to drop it into the stack without decomposing it into translation / rotation / scale parts.

SpatialSource.new_transform_matrix_source(my_gf_matrix)

For runtime-mutable matrices, build the source once and reach into source.source to swap the backing SpatialSourceBase, or rebuild the container when the matrix changes infrequently.

Composition order matters#

The stack is applied left-to-right (outer-to-inner). Two rules cover most cases:

  1. Sources that contribute relative transforms — translation, rotation, scale, matrix, look-at — combine with whatever preceded them.

  2. The prim-path source overrides preceding contributions, because it sets the full world transform of the underlying Transform node.

That asymmetry produces two common patterns:

# Billboard HUD anchored to a world point.
[translation, look_at_camera]

# Billboard label that follows a prim, lifted above its origin.
[prim_path, translation, look_at_camera]

If a panel ends up in an unexpected place, reorder the stack so the anchoring source comes first and any relative offsets follow.

Edge cases#

  • Per-frame sampling. look_at_camera and prim_path sources sample the camera and the stage every frame.

  • Missing prims. A prim-path source whose target does not yet exist will produce an identity transform until the prim is added; the source picks the prim up automatically on the next sample.

  • Reordering at runtime. space_stack is read once during construction. To change the placement strategy of an existing container, either reach into the source setter on individual sources (for like-for-like swaps) or rebuild the container.

See also#

  • SpatialSource — the class itself, including the constructor for custom SpatialSourceBase subclasses.

  • WidgetComponent — what the sources position.

  • Examples02_camera_facing_ui.py exercises look_at_camera; 03_prim_anchored_ui.py exercises prim_path together with translation and look_at_camera to demonstrate composition.