Repository navigation
scenix v1.9.0 — XR, Audio, And Physics Bridges
Scenix v1.9.0 — XR, Audio, And Physics Bridges
Scenix 1.9.0 adds three optional, no_std-capable CPU crates that bring
simulation-adjacent systems onto the scenix vocabulary without an engine,
OpenXR runtime, or audio SDK — scenix-xr (session planning, controller
bays, hand frames, anchors, planes, hit testing, light estimation),
scenix-audio (spatial mixing, Doppler, analysis, streamed buffer health),
and scenix-physics (a deterministic penalty-based rigid-body solver with
sensors, queries, sleeping, and a jump-capable character controller) — plus
CPU-bridge scene nodes, camera stereo math, a WebXR fallback plan,
debug helpers, and facade integration. The renderer pipeline itself is
untouched: everything springs off the same deterministic CPU rules whether
the host is a game loop, a wasm demo, or an XR runtime host.
Highlights
scenix-xris a standalone CPU crate (std/serde):XrSessionPlan
validates mode/reference-space/feature combinations with
XrSessionPlanErrordiagnostics and walks an explicit
Idle→Requesting→Starting→Active→Paused/Ending→Ended lifecycle
(XrSessionState::can_transition);XrControllerBaytracks both hands
from pointer/grip poses with velocity-based kinematic prediction;
XrHandFramemodels the 26-slot hand profile (JOINT_WRIST,
JOINT_THUMB_TIP,JOINT_INDEX_TIP) with tracked-joint accounting and
thumb-index pinch distance; anchors (XrAnchorStorewith age-based
pruning), planes (XrPlaneStore), hit testing (XrHitTestProbeagainst
floor/wall/window surfaces), and backward-compatible ambient-vs-light
estimation plan the field systems;XrFramePlancarries per-frame stats,
the swapchainXrViewport(tensor-scaled with theXR_MAX_VIEWPORT_SCALE
clamp), and builds the stereo rig directly.scenix-audiois a standalone CPU crate:AudioListener::axesbuilds the
orthonormal listeners;compute_spatial_decisionfolds pan weights,
four attenuation models (None/Linear/Inverse/InverseSquarewith
gain-clamped far curves), WebAudio-convention cones, and
DopplerModel::shift(speed-of-sound ratios, factor0..=1, clamped to
0.25..=4) into oneSpatialDecision;compute_spatial_gainfinishes
the stereo route.AudioStreamPlantracks chunk buffering health
(accept_chunk/promote_pending/consume_chunk+ underrun counting) and
AudioAnalysermeters mono/stereo input into RMS/peak plus three energy
bands with smoothing. Constants likeSPEED_OF_SOUND,STREAM_MAX_CHUNKS,
andAUDIO_LISTENER_MAX_SOURCESbound the field sizes.scenix-physicsis a standalone CPU crate:PhysicsWorld::earthruns a
penalty-based solver — O(n²) AABB broad phase with collider groups,
analytic narrow phase (sphere/box/capsule/plane pairs), restitution plus
Coulomb friction plus a Baumgarte positional push (CONTACT_RESOLVE_PERCENT
0.2,CONTACT_SLOP5 mm), real substeps (MAX_SUBSTEPS4 at ≤
MAX_SUBSTEP_DT1/120 s), linear speed clamping, and frame-threshold
sleeping with wake events — all deterministic against body ordering.
Colliderattaches with sensor/density controls for group-filtered
triggers;cast_ray/overlap_sphereanswer spatial questions;
CharacterState::move_and_slidewalks a kinematic capsule with slope
limits, step offsets, jumps, and probe-based ground snapping;
PhysicsTransformSyncbatches the per-frame write list for scene
integration.scenix-sceneaddsNodeKind::XrAnchor{anchor_id},
NodeKind::AudioSource{source_id, listener_id}, and
NodeKind::PhysicsBody{body_id, collider_id}riders on the scene schema;
scenix-coreadds the matchingXrSessionId,XrAnchorId,
AudioSourceId,AudioListenerId,PhysicsWorldId,PhysicsBodyId,
andColliderIdnewtypes.scenix-cameragains the stereo surface:XrFovfour-angle eye frusta,
XrEyeProjection(symmetric helper plus the raw WebXR layout) producing
WebGPU-convention projection matrices, andXrCameraRigcoupling a head
transform with eye separation into left/right view and view-projection
matrices.scenix-wasmgains anxrfeature and the front-end fallback plan:
webxr_fallback_plan(granted)splits granted runtimes from the desktop
emulation,emulated_headset_rigbuilds the standing desk stereo rig, and
emulated_controller_poseparks a fallback controller down-forward from
the head — the demo never dead-ends whennavigator.xris missing.scenix-helpersadds the debug pass surface:PoseAxesHelperdraws an
oriented triad (plus a-Zforward stick) for controllers and head rigs,
HandSkeletonHelperwires the 26-slot hand profile through
HAND_LINKS, and (behind the newphysicsfeature)
ColliderDebugHelperwireframes sensors apart from solid colliders via
debug_wireframe/debug_wireframe_world.- The facade gains
xr,audio,physics, andbridgesfeatures. The
bridge re-exports are curated lists because their module namespaces
(xr,audio,physics,world,sync) would otherwise shadow the
scene/input glob re-exports;scenix-mathgainsfloor/ceil/round
shims sono_stdsubsteps and viewport math round identically on every
target.
Small Example
use scenix::{
AttenuationModel, AudioListener, AudioStreamPlan, AudioSource, CharacterInput,
CharacterState, ColliderShape, DopplerModel, PhysicsBody, PhysicsWorld, PlaybackState, Quat,
RigidBodyKind, StreamFormat, Transform, Vec3, XrControllerBay, XrFeatures, XrHand, XrPose,
XrReferenceSpace, XrSessionPlan, XrSessionState, XrTrackingStatus,
};
// 1. XR: validate and present a room-scale VR plan.
let mut xr = XrSessionPlan::immersive_vr()
.with_reference_space(XrReferenceSpace::Stage)
.with_features(XrFeatures::room_scale_vr());
xr.validate()?;
for state in [
XrSessionState::Requesting,
XrSessionState::Starting,
XrSessionState::Active,
] {
xr.advance(state)?;
}
// Controller bays track pointer/grip poses; prediction extrapolates them.
let mut bay = XrControllerBay::new();
bay.update(
XrHand::Right,
XrPose::from_position(Vec3::new(0.25, 1.0, -0.6)),
XrTrackingStatus::Tracked,
);
// 2. Audio: face a listener down-range and let the spatial mix decide.
let listener = AudioListener::axes(Vec3::new(0.0, 1.5, 2.0), Vec3::NEG_Z, Vec3::Y);
let mut source = AudioSource::new(1, PlaybackState::Playing);
source.position = Vec3::new(-1.0, 1.0, -1.0);
let decision = scenix::compute_spatial_decision(
&listener,
&source,
AttenuationModel::Inverse,
1.0,
&DopplerModel::default(),
);
assert!(decision.audible);
// 3. Physics: walk a capsule across a floor.
let mut world = PhysicsWorld::earth();
world.add_solid(
PhysicsBody::new(Transform::IDENTITY).with_kind(RigidBodyKind::Static),
ColliderShape::boxy(20.0, 0.5, 20.0),
);
let player = world.add_body(
PhysicsBody::new(Transform::new(Vec3::new(0.0, 1.4, 0.0), Quat::IDENTITY, Vec3::ONE))
.with_kind(RigidBodyKind::KinematicPosition),
);
world.attach_collider(player, ColliderShape::capsule(0.35, 0.55));
let mut controller = CharacterState::new(player, 0.35, 0.55);
controller.move_and_slide(&mut world, &CharacterInput::move_towards(Vec3::X, 3.0, 1.0 / 60.0));
// 4. Streamed audio health stays deterministic for music-reactive scenes.
let mut plan = AudioStreamPlan::new(4, StreamFormat::PcmF32, 48_000, 2);
plan.accept_chunk();
assert!(plan.promote_pending());Feature gates: xr, audio and physics are independent facade features;
bridges enables all three at once; scenix-wasm's xr feature carries
the browser fallback; scenix-helpers' physics feature carries the
collider wireframe helper. The renderer WGSL is untouched in this release.