Skip to content

hl2-vr: multiple fixes - #397

Open
skryvel wants to merge 6 commits into
Supreeeme:mainfrom
skryvel:hl2-vr-mod-things
Open

hl2-vr: multiple fixes#397
skryvel wants to merge 6 commits into
Supreeeme:mainfrom
skryvel:hl2-vr-mod-things

Conversation

@skryvel

@skryvel skryvel commented Aug 6, 2026

Copy link
Copy Markdown

Continuation of #395, branch was deleted by accident; all review feedback from there is hopefully addressed.

Status: good for review
New:

  • fourth iteration: no new throwing improvement, seems as good as it gets with the workaround; the PR has been split, not sure if there's more worth splitting
  • third iteration: two pieces split off into new pr's, feedback addressed, throwing improved, commit from Serious Sam 3 PR removed
  • second iteration: some work dropped in favor of (better) work from another live PR, throwing is now also fixed(ish), and a commit from a Superhot VR fix (not PR'ed yet) that helped something here that I forgot. Rust version also bumped because from_hours. (deferred)

Played Half-Life 2: VR Mod (32-bit) end to end and fixed everything hit along the way. Each commit stands alone. Tested on Quest 3 via WiVRn, under both oculus/touch_controller and meta/touch_controller_plus (WiVRn picked different profiles across sessions). KDE/Wayland/Proton/D3D9. PR is a bit big, happy to split or whatever, just let me know.

Test session time: 15m + troubleshooting iterations (early chapter 2).

Binding hints showed "UNBOUND" for everything

HL2VR resolves its %+command% hint tokens through GetActionOrigins + GetOriginTrackedDeviceInfo, both stubs. Games read the hand from devicePath (compared against GetInputSourceHandle results) and the button from rchRenderModelComponentName.

Before: every hint reads "UNBOUND ..." (or renders uninitialized buffer contents).
After: GetActionOrigins, GetOriginLocalizedName and GetOriginTrackedDeviceInfo are implemented, backed by xrEnumerateBoundSourcesForAction / xrGetInputSourceLocalizedName, including the internal threshold/toggle/grab actions that custom bindings resolve to. Component names come from the SteamVR render model jsons matching each profile's render_model_name. Hints show the right buttons.

Throwing

Noticed throwing was a bit off and eventually got it workable by simulating the velocity vector from positions. Not perfect but also with HL2 janky physics I can't tell. It got pretty decent in the end.

Smaller fixes

  • fakexr implements xrEnumerateBoundSourcesForAction / xrGetInputSourceLocalizedName so the origin APIs are testable. New tests cover the grab slot, origin queries, tip pose, and zero sample count.
Screenshots

Bound keys.
image

Recordings

Variety.

demo1.mp4

Slow. You'll notice that upon dropping the object rotates. Apparently that's just HL2 and nothing we can do here, but the extra rotation and lack of velocity are fixed or greatly improved.

demo2.mp4

@skryvel

skryvel commented Aug 6, 2026

Copy link
Copy Markdown
Author

Ahh the commit from #394 got left in here together - let me know. I should add, about the throwing and the workaround and possible upstream issues: I'm far from knowledgeable on this topic so apply salt - all I can really vouch is it improved the HL2 situation.

@prikhi prikhi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my attempt to help ya out where i can, didn't look at much into the input stuff tho

Trackpad => c"trackpad",
A => c"button_a",
B => c"button_b",
_ => return None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should Menu & Select be sys_button?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite — Menu and Select aren't in this profile's legal_paths! (it has A, B, trigger, squeeze, thumbstick, trackpad), so those arms are unreachable and mapping them wouldn't do anything. I don't own an Index though, so if you think it should be wired differently I'll defer to you. Left a comment noting why they're unmapped.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not (in theory) allowed for games, but games still use menus and systems anyways, and SteamVR allows this. So even if the bindings get ignored, the render model should still return.

Comment thread src/system.rs Outdated
Comment on lines +584 to +587
// Some games sniff the HMD's tracking system/model/vendor strings to
// decide which motion controller scheme to enable. If these are empty,
// such games fall back to "no supported controllers" and ignore all
// controller input while menus still work via mouse.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is only used for controller schemes, can we pull it from the controller profile so these games don't default to q3 controllers for all devices?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mixing things I added #394 (comment) as response

Comment thread src/input/devices.rs Outdated
fn synthesized_velocity_enabled() -> bool {
use std::sync::OnceLock;
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| match std::env::var("XRIZER_SYNTHESIZED_VELOCITY") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we make XRIZER_SYNTHESIZED_VELOCITY into a constant that lives in the quirks module? maybe the MAX_SYNTHESIZED_SPEED constant below this too?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ but moved to other PR

Comment thread src/input/devices.rs
} else {
usize::MAX
};
let max_generic_trackers = std::env::var("XRIZER_MAX_TRACKERS")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an Env Vars section in the readme that it would be good to add usage/user docs for all these to

Comment thread src/lib.rs Outdated
// Multiple processes (e.g. a launcher's VR probe and the game itself)
// can run concurrently; a shared truncating log file lets one clobber
// the other's output, so give each process its own file.
let path = path.join(format!("xrizer-{}.txt", std::process::id()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be nice to have a get_logfile_path util function since this is used here & in the clipboard bit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe xrizer-<loginit-timestamp>-<xrizer-pid>.txt would give you nicer name sorting when you're looking for a latest log across system restarts

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ but moved to other PR

Comment thread src/lib.rs Outdated
Comment on lines +194 to +196
// Some launch setups don't deliver RUST_LOG to the game process
// (e.g. launchers that sanitize the environment), so fall back to
// a filter file next to the logs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to doc this next to the RUST_LOG docs in the readme

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ but moved to other PR

Comment thread src/lib.rs Outdated
Comment on lines +107 to +109
/// Remove old per-process log files so the log directory doesn't accumulate
/// one file per xrizer process forever. Files newer than the cutoff are kept,
/// as they may belong to a concurrently running process.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe fixes #73 too

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ but moved to other PR

@skryvel
skryvel force-pushed the hl2-vr-mod-things branch 2 times, most recently from 0cab5a2 to a0f8d5c Compare August 7, 2026 17:04
@skryvel

skryvel commented Aug 7, 2026

Copy link
Copy Markdown
Author

Iterated on the above. Not ready for rereview, still improving the throwing. Now I can throw books and heavy objects, plus dropping slowly doesn't jolt as much.

@skryvel skryvel changed the title hl2-vr: multiple fixes (fixes #266, #369) hl2-vr: multiple fixes (fixes #266, #369, probably #73) Aug 7, 2026
@skryvel
skryvel marked this pull request as draft August 7, 2026 17:14
@Mr-Zero88

Mr-Zero88 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@skryvel as mentioned here: #395 (comment)
could you split the chnages into spaerate PRs to make the merge and review process easier.

@skryvel
skryvel force-pushed the hl2-vr-mod-things branch from a0f8d5c to 10b1a38 Compare August 8, 2026 03:00
@skryvel skryvel changed the title hl2-vr: multiple fixes (fixes #266, #369, probably #73) hl2-vr: multiple fixes (also probably fixes #73) Aug 8, 2026
@skryvel

skryvel commented Aug 8, 2026

Copy link
Copy Markdown
Author

Split two pieces off. I'll split some more after. Still studying the throwing. Seems fine, but I haven't experimented enough.

skryvel added 2 commits August 8, 2026 00:36
…ceLocalizedName

Bound sources are derived from the suggested bindings for each hand's
current interaction profile. Localized names are built deterministically
from the source path so tests can assert on them.
Implement GetActionOrigins (via xrEnumerateBoundSourcesForAction,
including the internal threshold/toggle/grab actions custom bindings
resolve to) and GetOriginLocalizedName (via
xrGetInputSourceLocalizedName with a path-derived fallback).

GetOriginTrackedDeviceInfo now resolves full source-path origins to the
owning hand, reports the render model component name for the origin
(what games like HL2VR use to label bindings; names taken from the
matching SteamVR render model jsons per profile), and fills devicePath
with the hand's device path handle rather than the origin itself, as
games compare it against GetInputSourceHandle results to tell hands
apart.

The input path parser now also accepts OpenXR-style names (squeeze,
menu, thumbrest, force) so runtime source paths can be parsed.
@skryvel

skryvel commented Aug 8, 2026

Copy link
Copy Markdown
Author

Added recordings to description.

SUPERHOT VR binds its menu action to a long press of the X button. The
binding parser did not know the slot and silently dropped it, leaving
the menu unreachable on touch-style controllers. Approximate a long
press as a regular click rather than losing the binding.
@skryvel

skryvel commented Aug 8, 2026

Copy link
Copy Markdown
Author

Split more chunks into #401

@skryvel skryvel changed the title hl2-vr: multiple fixes (also probably fixes #73) hl2-vr: multiple fixes Aug 8, 2026
@skryvel
skryvel marked this pull request as ready for review August 8, 2026 15:05
@skryvel
skryvel force-pushed the hl2-vr-mod-things branch from 10b1a38 to 75fe426 Compare August 8, 2026 15:09
skryvel added 3 commits August 8, 2026 12:29
Generic trackers identify as Vive Trackers, which some games sniff to
pick a controller scheme: SUPERHOT VR switches to its Vive scheme when
it sees one, where dropping held items requires a trackpad that
touch-style controllers do not have. Add a small quirks table keyed on
the game executable's name (Wine rewrites argv[0] to the .exe path, so
this also works under Proton) and suppress generic trackers for
SUPERHOT VR. XRIZER_MAX_TRACKERS overrides the default either way.
Log analog/digital action state by name and the skeletal summary finger
curls (tagged with whether they came from hand tracking or the
estimated skeleton) at trace level. Entered-function traces alone can't
answer what data a game actually saw; these made it possible to rule
out binding and skeletal issues while debugging SUPERHOT VR.
WiVRn reports hand linear velocities at ~0.43x the speed implied by its
own position stream (measured over thousands of trace samples; direction
is accurate). Games that compute throws from controller velocity feel
like everything is glued down - most throws die mid-air.

Add a synthesized_velocity quirk (on for Half-Life 2: VR) that replaces
the runtime's linear velocity with a finite difference over the last
~60ms of positions, capped at a plausible hand speed so tracking
glitches don't become rocket throws. XRIZER_SYNTHESIZED_VELOCITY=1/0
overrides the quirk either way. Position-derived velocities stay correct
on runtimes without the problem, just redundant.
@skryvel
skryvel force-pushed the hl2-vr-mod-things branch from 75fe426 to 423fd8c Compare August 8, 2026 15:42
@skryvel
skryvel requested a review from prikhi August 8, 2026 15:49
@Mr-Zero88

Copy link
Copy Markdown
Contributor

@skryvel could you split of 17827f7 and 13423df into a pr adds implement action origin queries for binding hint

and 4a46bbe into its own pr as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants