Skip to content

About

A lightweight C++ runtime for turning OpenUSD stages into interactive, real-time worlds.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

OpenUSD Stage Runner

CMake CI License: Apache-2.0 OpenUSD: 26.08

usd-stage-runner is an experimental C++ runtime that opens an OpenUSD Stage, derives a transient Runtime World from its prims, and advances that world in a bounded real-time update loop. The project is being delivered as small vertical slices; physics, character control, and first- and third-person camera following with collision avoidance are implemented.

The project is a lightweight runtime orchestration layer, not the owner of a physics implementation. Reusable physics contracts and the Jolt backend are consumed from installed usd-physics-plugins packages; Stage Runner retains Runtime World, fixed-step scheduling, gameplay policy, Stage-specific import, host lifecycle, and discardable USD synchronization. Installed physicsUsd supports standard Box declarations by default through pinned Windows/Linux packages; broader support remains in progress. See the physics repository boundary.

The intended architecture and the distinction between implemented and planned behavior are documented in docs/README.md.

Quick start: usdview via OpenStrata

From the repository root in PowerShell, use an OpenUSD 26.08 install with usdview and Python 3.13, matching strata.lock. On Windows, python must be on PATH because the OpenUSD usdview.cmd launcher calls it. Replace the example install path with your own:

$usdRoot = 'C:\path\to\openusd-with-usdview'
$env:CMAKE_PREFIX_PATH = 'C:\path\to\jolt-install'
ost runtime pull cy2026 --profile usd --from-usd $usdRoot
ost runtime pull cy2026 --profile lookdev --from-usd $usdRoot
ost library pull
ost build --intent physics-usd
$fixture = (Resolve-Path .\tests\fixtures\standard_character_follow_camera.usda).Path
ost plugin view plugins/runnerSchema $fixture --profile lookdev

If the runtimes and bundle are already built, only the last two lines are needed. Choose Stage Runner > Play, then use WASD to move PlayerCube. The fixture path is absolute because ost plugin view resolves relative fixture paths from the bundle directory.

The pinned Jolt-enabled physics artifacts require their matching Jolt SDK on CMAKE_PREFIX_PATH. WASD and the arrow keys move the cube; Space jumps. Play selects the scene's third-person rig when usdview is using its free camera. The camera then follows the cube. If you have already chosen another scene camera, Play keeps that choice.

Current capabilities

  • runtimeCore, with an injectable frame clock, bounded fixed-step accumulator, a host-facing play-session controller for play, pause, stop, single-step, and reset, a prim-indexed component registry, Runtime World, runtime transforms, and a dirty synchronization queue;
  • inputCore, with named action state and backend-neutral movement intent;
  • external physicsCore, with typed backend-neutral resource handles, descriptors for boxes, bodies, and fixed constraints, fixed-step commands, changed-body extraction contracts, character ground and collision-segment query extensions, and prim/body runtime synchronization;
  • characterCore, with backend-neutral character intent and controller state, walkable-ground and slope evaluation, desired motion, facing, jump-edge handling, and deterministic tests against a physics test double;
  • cameraCore, with prim-indexed target and optional anchor identities, free, first-person, third-person, and orbit modes, deterministic target following, optional third-person collision probes, live pose state, exponential smoothing, and dirty Runtime World translation updates without OpenUSD, Jolt, or a renderer;
  • vehicleCore, with normalized throttle, brake, steering, and handbrake intent; explicit chassis identity; independently composed steering, powertrain, service-brake, and handbrake configuration; and deterministic per-wheel command distribution that does not assume four wheels;
  • external physicsJolt, which owns Jolt initialization and resource lifetime, creates box shapes and static or dynamic bodies, advances fixed simulation steps, extracts changed body state, and implements character ground shape casts and camera collision ray casts without exposing Jolt types publicly;
  • runnerSchema, a codeless OpenUSD plugin defining the single-apply RunnerPhysicsBodyAPI, RunnerColliderAPI, RunnerCharacterAPI, and RunnerCameraRigAPI declaration contracts;
  • stageRuntime, a reusable OpenUSD-facing play session that owns the prim/body bridge, imports the Runtime World and physics, character, and camera systems, owns fixed-step execution and reset/rebuild semantics, and incrementally synchronizes dirty transforms into a discardable anonymous runtime layer for any host without changing persistent authored layers;
  • inputSdl, which maps WASD, arrow keys, and the first gamepad's left stick to move.x and move.y, and maps Space or the gamepad south button to jump, without exposing SDL types to core consumers;
  • stage_runner, a thin standalone adapter that opens a Stage, selects Jolt and SDL adapters, polls input, and drives the shared stageRuntime session for an explicit frame bound;
  • usdviewStageRunner, a Python usdview menu and timer adapter backed by a native binding to the same StageSession, with play, pause, stop, single-step, and reset controls and bundled Runner schema registration;
  • minimal transform, falling-cube, character-import, runnable walk-and-jump, and obstructed first-/third-person camera-follow USDA fixtures plus dependency-free unit tests; and
  • dual build paths through plain CMake and OpenStrata.

The character-control, camera-rig, and host-integration milestones are implemented end to end. The physics core and Jolt backend have been extracted, their pinned Windows/Linux packages are publicly available, and Stage Runner's package migration has passed hosted verification. Standard Box import has passed Windows/Linux parity against the published parser pins. Vehicle physics application remains paused until standard physics delivery and shared MMD/VRM validation advance. Behavior and OpenExec integration are later slices.

Build with OpenStrata

OpenStrata supplies the pinned OpenUSD runtime and compiler environment. CI uses ost 0.23.1; use that version locally when reproducing its checks:

$runtimeArtifact = 'sha256:ebb0c7da509ee14ada19ee5b461de6996aad0024b5c9640f12dde76912e849b5'
ost artifact pull 'oci://ghcr.io/animu-sphere/openstrata-runtime-cy2026-usd@sha256:d3ff79a6f330558c3b9a427a927d340fe3dcab1fe89107faa0ea9f66a104b7bf' `
  --expect-artifact $runtimeArtifact --require-kind runtime
ost runtime pull cy2026 --profile usd --from-artifact $runtimeArtifact --force
$libraries = (ost library pull --json | ConvertFrom-Json).data.libraries
$externalPrefixes = @($libraries | ForEach-Object { $_.prefix })
$env:CMAKE_PREFIX_PATH = ($externalPrefixes + $env:CMAKE_PREFIX_PATH) -join ';'
ost build
ost test

The migration pins both the physics artifact content digests and immutable public OCI source URIs. After materializing the matching pinned runtime as shown above, ost library pull can populate a fresh OpenStrata cache without repository-local package builds.

The reusable core can also be built and tested as an isolated OpenStrata library member:

ost library build libs\runtimeCore
ost library test libs\runtimeCore

To run the staged executable directly, first enter the runtime-activated shell:

ost devshell cy2026 --profile usd

Then run the deterministic smoke path inside that shell:

.\apps\stage_runner\bin\stage_runner.exe tests\fixtures\standard_falling_cube.usda --frames 180 --deterministic

Build with plain CMake

A C++17 compiler is sufficient for runtimeCore. The complete repository build requires installed physicsCore and physicsJolt packages, plus physicsUsd when building Stage integration with its default standard importer. Point CMAKE_PREFIX_PATH at those packages and OpenUSD; a Jolt-enabled physicsJolt package also requires its Jolt SDK dependency to be discoverable:

cmake --preset dev -DCMAKE_PREFIX_PATH="C:\path\to\usd-physics-plugins;C:\path\to\jolt;C:\path\to\openusd"
cmake --build --preset dev
ctest --preset dev

Standard Box import is enabled by default. Obtain the published parser with ost library pull, or install the sibling repository with USDPHYSICS_BUILD_USD=ON, then add its prefix and the same OpenUSD SDK to CMAKE_PREFIX_PATH. Try tests/fixtures/standard_falling_cube.usda or tests/fixtures/standard_character_follow_camera.usda with either host. See the supported subset.

ost build --intent physics-usd and ost test --intent physics-usd require the same standard import and stage the usdview adapter. The intent requires OpenUSD and a Jolt-enabled installed backend. ost library pull now resolves published Windows/Linux physicsUsd packages through exact content and OCI digests. The CI gate consumes those binaries through default-enabled plain CMake, an explicit compatibility-only build, and this intent. Set USD_STAGE_RUNNER_ENABLE_PHYSICS_USD=OFF for a compatibility-only build without the parser; standard bodies then produce an enable-option diagnostic. See the public-pin report.

The two core/backend physics packages are required at configure time. Without OpenUSD, the host still compiles but reports that Stage loading is unavailable; the backend-neutral unit tests remain buildable. Set USD_STAGE_RUNNER_REQUIRE_OPENUSD=ON when a missing SDK should be a configure error. Interactive input is enabled when CMake can find SDL3::SDL3 or SDL2::SDL2; set USD_STAGE_RUNNER_REQUIRE_SDL=ON to require a real SDL-backed demo build. The OpenStrata usd profile does not currently bundle SDL, so pass an SDL package through CMAKE_PREFIX_PATH for interactive builds. Set USD_STAGE_RUNNER_REQUIRE_JOLT=ON to reject an installed physicsJolt package whose exported metadata reports that the backend was built without Jolt support. Otherwise that package remains usable for honest unavailable-backend behavior.

usdview plugin

The usdview adapter is built when OpenUSD includes Python support. Add the generated package parent to PYTHONPATH and the package directory containing plugInfo.json to PXR_PLUGINPATH_NAME:

$env:PYTHONPATH = "$PWD\build\cy2026-windows-x86_64-py313-usd\plugins\usdviewStageRunner\python;$env:PYTHONPATH"
$env:PXR_PLUGINPATH_NAME = "$PWD\build\cy2026-windows-x86_64-py313-usd\plugins\usdviewStageRunner\python\usdviewStageRunner;$env:PXR_PLUGINPATH_NAME"
usdview tests\fixtures\standard_character_follow_camera.usda

The Stage Runner menu exposes Play, Pause, Stop, Single Step, and Reset. After Play, click the viewport and use WASD or the arrow keys to move /World/PlayerCube; Space requests a jump in a Jolt-enabled character Stage. Stop and Reset discard the plugin-owned anonymous runtime layer; they do not change persistent authored layers. See the plugin README for layout and runtime defaults.

OpenStrata Plugin View

The plugin-view build intent stages that same usdview package and native StageSession binding into the runnerSchema bundle. OpenStrata then supplies the bundle's Python, plugin-discovery, and loader paths. Use the quick-start command above to launch it.

The plugin-view intent stages the adapter inside runnerSchema. OpenStrata 0.23.0 added a first-class usdview-plugin bundle for --with composition, but this repository's pinned usd profile does not promise usdview. Moving the adapter to its own bundle also requires a runtime artifact with the usdview capability and corresponding CI coverage.

standard_third_person_camera.usda can be opened the same way. The selected OpenStrata runtime must contain usdview, and the installed physicsJolt package must have backend support to execute physics declarations; otherwise the shared adapter reports the same unavailable-backend error as stage_runner and ordinary usdview.

Host usage

stage_runner <scene.usd[a|c]> [--frames N] [--fixed-dt SECONDS]
             [--max-fixed-steps N] [--deterministic]
             [--move-x VALUE] [--move-y VALUE] [--jump]

The default loop runs 300 frames at a 60 Hz target. --deterministic injects one fixed interval per frame and does not sleep, making integration tests fast and repeatable. --move-x and --move-y accept normalized values from -1 to 1, and --jump holds the jump action for deterministic adapter-to-Stage tests. All three require --deterministic. Interactive runs use SDL keyboard and gamepad input.

The current compatibility importer expects physics prims to apply both RunnerPhysicsBodyAPI and RunnerColliderAPI. The target authored model uses standard UsdPhysics; existing Runner declarations remain documented here until that migration is implemented. runner:physics:motionType accepts static or dynamic, mass is authored with runner:physics:mass, and the initial collider contract uses runner:physics:shape = "box" plus positive local-space runner:physics:halfExtents. Ordered scale ops multiply those extents. A Stage with physics declarations must be Y-up with metersPerUnit = 1. Physics prims require one translate op followed only by scale ops, plus identity ancestor transforms unless they set resetXformStack. A build with both OpenUSD and Jolt is required to simulate the declarations. Authored runner:physics:* attributes without their owning API schema are rejected as legacy data.

Character prims additionally apply RunnerCharacterAPI to the same dynamic physics prim. runner:character:groundProbeDistance, runner:character:maximumSlopeAngleRadians, and runner:character:jumpSpeed configure the prim-indexed runtime controller. Character attributes without RunnerCharacterAPI, characters without both physics APIs, and static character bodies are rejected.

Camera prims apply RunnerCameraRigAPI. runner:camera:target and the optional runner:camera:anchor are prim relationships; non-free modes require exactly one target. Mode, offset, distance, pitch, yaw, damping, collision enablement, and collision clearance are read into a prim-indexed camera rig. The importer rejects non-camera application sites, unresolved or non-xformable references, invalid values, and authored camera properties without their owning API schema. Camera declarations currently require a Y-up Stage. Camera, target, and anchor translations must already be representable in world space by their imported local RuntimeTransform; non-identity ancestor transforms require resetXformStack until composed camera transforms are supported. Rig Camera prims accept an empty transform stack or one translate op optionally followed by the reserved double-precision xformOp:orient:runnerCamera; other authored transform ops are rejected so the runtime pose is not composed with an unknown rotation. A rig may not target or anchor itself. At each fixed step, imported rigs evaluate after physics extraction. Changed camera poses use the shared dirty queue to update translation and the reserved orientation only. Collision-enabled third-person rigs use the optional backend-neutral segment query, ignore a bound target body, shorten the desired camera distance by the authored clearance, and smooth the collision-adjusted pose.

License

Project code is licensed under Apache-2.0.

About

A lightweight C++ runtime for turning OpenUSD stages into interactive, real-time worlds.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages