aether-input
VR input abstraction with OpenXR and desktop fallback
The aether-input crate provides input handling for VR controllers, hand tracking, desktop keyboard/mouse, and haptic feedback. It abstracts over OpenXR for VR headsets and provides a desktop adapter for development without a headset.
Overview
The input system is built around a pipeline that converts raw device data into high-level interaction events. It supports:
- OpenXR session management with reference spaces and swapchain handling.
- Controller and hand tracking with joint-level skeletal data.
- Action mapping that binds physical inputs to logical actions.
- Locomotion modes including smooth movement, snap/smooth turn, and teleport.
- Haptic feedback dispatched to specific controller channels.
- Desktop fallback with configurable keyboard and mouse bindings.
- Dead zone and sensitivity processing for analog inputs.
Key Types
InputRuntime
The top-level runtime that processes raw input each tick and produces a PlayerInputFrame.
use aether_input::{InputRuntime, InputRuntimeConfig};
let config = InputRuntimeConfig {
backend: InputBackend::OpenXR,
comfort_profile: ComfortProfile::default(),
};
let runtime = InputRuntime::new(config);
OpenXrAdapter
Manages the OpenXR session lifecycle, tracking, and haptic output.
use aether_input::{OpenXrAdapter, SessionConfig, SessionState};
let adapter = OpenXrAdapter::new(SessionConfig::default());
SessionManager
Handles OpenXR session state transitions (idle, ready, synchronized, visible, focused).
use aether_input::{SessionManager, SessionState, ReferenceSpaceType};
let mut session = SessionManager::new();
// Transitions are driven by the OpenXR runtime
TrackingPipeline
Processes raw tracking data from controllers and hand joints into a TrackingSnapshot.
use aether_input::{TrackingPipeline, TrackingSnapshot, Hand, ControllerState};
let pipeline = TrackingPipeline::new();
let snapshot: TrackingSnapshot = pipeline.sample();
ActionMap
Maps physical inputs to named logical actions. Supports rebinding at runtime.
use aether_input::{ActionMap, ActionBinding, InputSource};
let mut actions = ActionMap::new();
actions.bind(ActionBinding {
name: "grab".into(),
source: InputSource::TriggerLeft,
threshold: 0.8,
});
LocomotionProfile
Configures movement style for VR comfort. Supports teleport, smooth movement, snap turn, and smooth turn.
use aether_input::{LocomotionProfile, LocomotionMode, ComfortStyle};
let profile = LocomotionProfile {
mode: LocomotionMode::SmoothMove,
comfort: ComfortStyle::VignetteOnMove,
speed: 3.0,
snap_angle_degrees: 45.0,
};
DesktopAdapter
Provides keyboard and mouse input for desktop development without a VR headset.
use aether_input::{DesktopAdapter, DesktopAdapterConfig, KeyCode};
let adapter = DesktopAdapter::new(DesktopAdapterConfig::default());
HapticDispatcher
Sends haptic pulses to VR controllers with configurable waveforms.
use aether_input::{HapticDispatcher, HapticRequest, HapticEffect, HapticChannel};
let mut haptics = HapticDispatcher::new();
haptics.submit(HapticRequest {
channel: HapticChannel::Left,
effect: HapticEffect::Pulse {
amplitude: 0.7,
duration_ms: 50,
},
});
Usage Examples
Processing a Frame
use aether_input::{
InputRuntime, InputRuntimeConfig, InputRuntimeInput,
InputRuntimeOutput, PlayerInputFrame,
};
let mut runtime = InputRuntime::new(InputRuntimeConfig::default());
// Each tick, feed raw state and receive processed input
let output: InputRuntimeOutput = runtime.step(&InputRuntimeInput {
dt: 0.011,
tracking: raw_tracking,
});
let frame: &PlayerInputFrame = &output.player_input;
Teleport Computation
use aether_input::{compute_teleport, TeleportAnchor, TeleportResult};
let result: TeleportResult = compute_teleport(
current_position,
aim_direction,
max_distance,
);