- docs/steamframe/README.md: entry point with status, branches/remotes, setup, commands, Steam Frame facts, open points. - CLAUDE.md: working rules and code conventions (fork-only). - docs/steamframe/OVRAS-FEATURES.md: OVRAS's features, how they are implemented, issues found while reading. - PROGRESS.md: status and wrap-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
198 lines
9.5 KiB
Markdown
198 lines
9.5 KiB
Markdown
# OVRAS: features and how they are implemented
|
||
|
||
Analysis of 2026-10-07, based on upstream `master` (`31df8b4`, 5.8.17). Line numbers refer to
|
||
that state. `qt6` has the same features, so the numbers are close. Paths are relative to
|
||
`src/`. Which of these features SteamVR now offers itself is in
|
||
[REQUIREMENTS.md](REQUIREMENTS.md), "Feature classification".
|
||
|
||
## Architecture
|
||
|
||
- **Dashboard overlay:**
|
||
- `VR_Init(VRApplication_Overlay)`, app key `steam.overlay.1009850`.
|
||
- QML UI (1200×800) rendered off-screen via `QQuickRenderControl`.
|
||
- The texture goes to `SetOverlayTexture`. On `steamframe` the render backends in `render/`
|
||
do this; before, it was an OpenGL FBO, and on `qt6` QRhi.
|
||
- **Input:** SteamVR mouse/scroll events are turned into Qt events (`overlaycontroller.cpp`,
|
||
`mainEventLoop()`).
|
||
- **Main loop (`OnTimeoutPumpEvents` → `mainEventLoop()`):**
|
||
- Timer; "vsync" mode once per compositor frame (`GetTimeSinceLastVsync`).
|
||
- Per tick:
|
||
1. input actions
|
||
2. VR events
|
||
3. poses
|
||
4. `eventLoopTick` of the tab controllers (always)
|
||
5. `dashboardLoopTick` (only while the dashboard is visible)
|
||
- **SteamVR Input:** `package_files/action_manifest.json`, 5 action sets:
|
||
- `motion`: space drag/turn, gravity, snap/smooth turn, height toggle, offsets, auto-turn
|
||
- `music`: media keys
|
||
- `misc`: axis locks, chaperone toggle, keyboard shortcuts
|
||
- `system`: push-to-talk, exclusive input, haptic clicks
|
||
- `haptic`: vibration, proximity sensor
|
||
|
||
Default bindings exist for Vive, Index, WMR, HP, Touch, generic HMD and Frame controllers.
|
||
- **Persistence:**
|
||
- App settings in a `QSettings` INI file (`settings/`). Profiles are stored as arrays
|
||
(`ISettingsObject`).
|
||
- SteamVR's own settings go directly through `vr::VRSettings()` (`openvr/ovr_settings_wrapper`).
|
||
- **Platform:**
|
||
- Windows: WASAPI / `IPolicyConfig`, `SendInput`.
|
||
- Linux: PulseAudio, X11 XTest, D-Bus MPRIS.
|
||
- Elsewhere: dummies.
|
||
|
||
## Features
|
||
|
||
### Playspace movement (`tabcontrollers/MoveCenterTabController.cpp`)
|
||
|
||
Almost everything shifts or rotates the **working standing zero pose** through
|
||
`IVRChaperoneSetup` and shows it live with `ShowWorkingSetPreview()`. Nothing is committed
|
||
per frame. This mechanism does **not work on the Frame** (inside-out tracking).
|
||
|
||
- **Core, `updateSpace()` (:2497–2721):**
|
||
- Offsets X/Y/Z and the rotation (centidegrees) are multiplied with the stored basis.
|
||
- The result goes to `SetWorkingStandingZeroPoseToRawTrackingPose`, in seated mode
|
||
additionally to the seated pose.
|
||
- Safety limit: about 40 km.
|
||
- **Space drag:**
|
||
- The controller's position delta, multiplied by `dragMult`, is added to the offsets.
|
||
- Axis locks; the speed is kept for fling.
|
||
- Comfort mode skips frames.
|
||
- **Space turn:** the change of the yaw angle via quaternions
|
||
(`q·conj(q_last)`, `quaternion/quaternion.h`). Rotation pivots on the HMD by default.
|
||
- **Snap/smooth turn:** fixed angle per press resp. per tick, so smooth turn depends on the
|
||
frame rate.
|
||
- **Gravity/momentum:** Euler integration with friction, terminal velocity 50 m/s, landing on
|
||
the floor, reversible.
|
||
- **Height toggle:** Y offset plus a new gravity floor.
|
||
- **Offset profiles, apply/reset:**
|
||
- "Apply" commits with `CommitWorkingCopy(Live)` (limit 990 m).
|
||
- `zeroOffsets()` / `updateChaperoneResetData()` also commit at start-up. On the Frame
|
||
this shifts the play area, hence R7.
|
||
|
||
### Rotation (`tabcontrollers/RotationTabController.cpp`, via `setRotation`)
|
||
|
||
- **Auto-turn:**
|
||
- Rotates the space when the user gets near a wall, either as a snap or smoothly.
|
||
- Direction: from the corner shared with the last wall, otherwise cord untangling (HMD
|
||
total rotation count).
|
||
- **Vestibular motion:** rotation as if walking on a circle of radius r
|
||
(`2·asin(d/2r)`).
|
||
- **View ratchetting:** amplifies head turns away from the wall.
|
||
|
||
### Fix floor / recenter (`tabcontrollers/FixFloorTabController.cpp`)
|
||
|
||
- **Fix floor:**
|
||
- Measures the lower controller over 25 frames.
|
||
- Subtracts the controller thickness per model and orientation (Vive / Knuckles).
|
||
- Commits the result as a Y offset; undo is possible.
|
||
- **Recenter:** `VRChaperone()->ResetZeroPose(...)`.
|
||
|
||
### Chaperone (`tabcontrollers/ChaperoneTabController.cpp`)
|
||
|
||
- **Settings:**
|
||
- Visibility, color, fade distance, wall height, center marker, floor bounds and style.
|
||
- These are keys in `k_pch_CollisionBounds_Section`.
|
||
- "Disable" sets the fade distance to 0 and remembers the old value.
|
||
- **Own center marker overlay:** shows the rotation count (cord).
|
||
- **Dim when crouching:** below a height, the fade distance goes to 0 over 10 s.
|
||
- **Proximity warnings:**
|
||
- Distance of HMD and controllers to the walls (`utils/ChaperoneUtils.cpp`: projection onto
|
||
each wall segment).
|
||
- Reactions: switch to the beginner style, vibration (own thread,
|
||
`TriggerHapticVibrationAction`), alarm sound with distance-dependent volume, open the
|
||
dashboard.
|
||
- **Chaperone profiles:**
|
||
- Save the geometry (`GetLiveCollisionBoundsInfo`, zero pose, size) and the settings.
|
||
- Apply them with `SetWorking…` + `CommitWorkingCopy`.
|
||
- Autosave profile.
|
||
|
||
### Video (`tabcontrollers/VideoTabController.cpp`)
|
||
|
||
- **Brightness:** a black overlay right in front of the HMD. Its alpha `(1−x)^(1/3)`
|
||
compensates for perception.
|
||
- **Color tint:**
|
||
- either a colored overlay (`SetOverlayColor`)
|
||
- or SteamVR's `HmdDisplayColorGainR/G/B`
|
||
- **SteamVR keys:** supersampling (incl. manual override), motion smoothing, supersample
|
||
filtering.
|
||
- **Video profiles.**
|
||
|
||
### Audio (`tabcontrollers/AudioTabController.cpp`, `audiomanager/`)
|
||
|
||
- **Interface:** `AudioManager` with a Windows, a PulseAudio and a dummy implementation.
|
||
- **Windows:**
|
||
- WASAPI/MMDevice, `IAudioEndpointVolume`.
|
||
- The default device is set via the undocumented `IPolicyConfig`.
|
||
- External changes come in via `IMMNotificationClient`.
|
||
- **Linux:**
|
||
- PulseAudio: `pa_context_set_default_sink/source`; existing source outputs are moved to
|
||
the new source.
|
||
- The original state is restored at exit.
|
||
- **No mirror device.**
|
||
- **Mirror device and device override:** these are SteamVR keys (`k_pch_audio_*`), not
|
||
OVRAS's own routing.
|
||
- **Push-to-talk / -mute:** SteamVR Input action → mic mute + notification overlay on the
|
||
HMD.
|
||
- **Mute the mic when the HMD is taken off:** proximity sensor action.
|
||
- **Audio profiles.**
|
||
|
||
### SteamVR tab (`tabcontrollers/SteamVRTabController.cpp`)
|
||
|
||
- **Toggles:** each is a SteamVR setting.
|
||
- performance graph, multiple drivers, do not disturb, no-HMD mode
|
||
- camera options
|
||
- system button to all apps, don't fade to grid, controller power auto-launch
|
||
- **Restart SteamVR:** Windows only, via `.bat`.
|
||
- **Per-game controller bindings:**
|
||
- Use SteamVR's web server `localhost:27062`, with a spoofed `Referer`.
|
||
- Saved as JSON and applied when the scene app changes.
|
||
- **Receiver/tracker pairing:** WebSocket to the lighthouse web interface (`start_pairing`).
|
||
- **`openvr/lh_console_util.cpp`** (`lighthouse_console`) is compiled but **not used**.
|
||
- There is no base-station power management.
|
||
|
||
### Utilities (`tabcontrollers/UtilitiesTabController.cpp`, `keyboard_input/`, `media_keys/`, `alarm_clock/`)
|
||
|
||
- **Keyboard:**
|
||
- Buttons (Enter, Alt-Tab, Ctrl-C/V, …) and 3 freely defined shortcuts.
|
||
- A parser for the syntax `^`=Ctrl, `*`=Alt, `>`=Shift, `#`=Super.
|
||
- Sent via Windows `SendInput` or X11 XTest. On the Frame (gamescope) the X11 path probably
|
||
doesn't reach games.
|
||
- **Media keys:**
|
||
- Windows: `VK_MEDIA_*`.
|
||
- Linux: D-Bus to every `org.mpris.MediaPlayer2.*`.
|
||
- **Tracker battery:** icon overlay on each tracker (`Prop_DeviceBatteryPercentage_Float`),
|
||
only while the dashboard is visible.
|
||
- **Alarm clock:** HMD overlay at a set time, shown for 3 s.
|
||
|
||
### Statistics (`tabcontrollers/StatisticsTabController.cpp`)
|
||
|
||
- **Frame counters:** `VRCompositor()->GetCumulativeStats()` gives presented, dropped,
|
||
reprojected and timed-out frames. "Reset" only stores an offset.
|
||
- **Distance traveled:** horizontal HMD movement, every 11th tick, steps ≥ 1 cm.
|
||
- **Rotation count, maximum controller speed.**
|
||
|
||
### Settings tab (`tabcontrollers/SettingsTabController.cpp`)
|
||
|
||
Autostart (`SetApplicationAutoLaunch`), native chaperone import, block Oculus SDK, desktop
|
||
mode, tick rate (vsync / custom tick rate).
|
||
|
||
## Issues found while reading (not verified)
|
||
|
||
| Place (master) | Issue |
|
||
|---|---|
|
||
| `MoveCenterTabController.cpp:1198` | `updateChaperoneResetData()` allocates `new vr::HmdQuad_t[]` on every call and never frees the old array (memory leak) |
|
||
| `MoveCenterTabController.cpp:2512` | `if ( m_chaperoneHasCommit ) { m_chaperoneHasCommit = true; … }` looks like a slip |
|
||
| `MoveCenterTabController.cpp:1332` | `leftHandSpaceDrag`: the final `else` overwrites the swap/override choice |
|
||
| `MoveCenterTabController` | Rotation is wrapped inconsistently (snap/smooth/auto-turn 0–36000 vs. ±18000 elsewhere) |
|
||
| `VideoTabController.cpp:114` | Sets the sort order of the brightness overlay instead of the color overlay |
|
||
| `ChaperoneTabController.cpp:903` | Floor bounds are read as Bool but written as Int32 |
|
||
| `MoveCenterTabController::zeroOffsets` | If `GetCalibrationState() == 200`, init never completes and the commit repeats every tick (with commits enabled) |
|
||
| `utils/FrameRateUtils.cpp`, `openvr/lh_console_util.cpp` | Compiled but unused |
|
||
|
||
Found on the `qt6` branch and fixed on `steamframe` (see PROGRESS.md):
|
||
- the build fails on Linux
|
||
- QRhi Vulkan path: wrong `m_nFormat` (Qt enum instead of `VkFormat`); no layout transition,
|
||
no SteamVR extensions, no choice of the HMD's GPU
|
||
- mouse y not flipped on Linux
|
||
- no `VR_Shutdown`
|
||
- hard-coded version 5.7.4
|