From 38b7f89d74fd580bc57e3c038812c0354bda7cec Mon Sep 17 00:00:00 2001 From: iChris4 Date: Fri, 25 Sep 2026 00:42:48 +0200 Subject: [PATCH] Take item pinches only from a mostly open hand - Now that holding the wheel engages the race controls, closing the other hand on the rim could bring thumb and index together on the way and fire an item. A pinch uses an item only while the hand's grasp is under 0.5; a relaxed hand reads about 0, a hand closed on a rim about 0.9. Co-Authored-By: Claude Opus 5.5 --- OPENXR.md | 5 +++-- runtime/include/vr/openxr_hand_tracking.h | 9 +++++++++ runtime/src/vr/openxr_input.cpp | 4 ++-- runtime/tests/vr_hand_tracking_tests.cpp | 5 +++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/OPENXR.md b/OPENXR.md index 25db266..af6a022 100644 --- a/OPENXR.md +++ b/OPENXR.md @@ -544,8 +544,9 @@ summed over each finger's three joints, read as 0 below 1.2 radians (a relaxed h hand on the rim takes hold. The grasp never reaches the game's buttons (as a squeeze it would press the gamepad's shoulders). In the cockpit, while a bare hand holds the wheel it holds the gas (A, the gamepad's South) until both hands let go, and a pinch from a free bare hand, either one, uses -an item (Z, the gamepad's L) if the hand has been off the wheel for 0.15 s, so opening a hand off -the rim does not fire one; holding the pinch holds the button. The left palm-up pinch pauses (a +an item (Z, the gamepad's L) if the hand has been off the wheel for 0.15 s and is still mostly open +(a grasp under 0.5), so neither opening a hand off the rim nor closing one on it fires one; +holding the pinch holds the button. The left palm-up pinch pauses (a pinch made in that gesture is not an item: `XR_FB_hand_tracking_aim` reports the system gesture). With no bare hand on the wheel, in the cockpit or anywhere else, a right pinch is A and the pointer follows the hand's aim ray, which is what the pause menu and the results need; in a race diff --git a/runtime/include/vr/openxr_hand_tracking.h b/runtime/include/vr/openxr_hand_tracking.h index 1ec0a6e..573501d 100644 --- a/runtime/include/vr/openxr_hand_tracking.h +++ b/runtime/include/vr/openxr_hand_tracking.h @@ -214,6 +214,15 @@ private: bool m_blocked = false; }; +// A pinch uses an item only from a hand still mostly open: a hand closing on +// the rim can bring thumb and index together on the way, and a fist is not a +// pinch. +inline constexpr float kItemPinchMaxGrasp = 0.5f; + +inline bool ItemPinch(bool pinch, float grasp) noexcept { + return pinch && IsFinite(grasp) && grasp < kItemPinchMaxGrasp; +} + // A hand's pinch and menu gesture this frame. The runtime's own recognition // (XR_FB_hand_tracking_aim) is preferred when it is valid, and a pinch made // while the hand is in the system gesture (palm towards the face) is the menu diff --git a/runtime/src/vr/openxr_input.cpp b/runtime/src/vr/openxr_input.cpp index 9474afc..bb467f9 100644 --- a/runtime/src/vr/openxr_input.cpp +++ b/runtime/src/vr/openxr_input.cpp @@ -992,8 +992,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen& if (bare_held[0] || bare_held[1]) { std::array item_pinch{}; for (uint32_t hand = 0; hand < kHands; ++hand) { - item_pinch[hand] = m_hand_driven[hand] && m_pinch_gate[hand].Update(m_pinch[hand], m_wheel_held[hand], - dt_seconds); + const bool pinch = hand_tracking::ItemPinch(m_pinch[hand], m_driving.hands[hand].grasp); + item_pinch[hand] = m_hand_driven[hand] && m_pinch_gate[hand].Update(pinch, m_wheel_held[hand], dt_seconds); } hand_tracking::ApplyBareHandRace(hands, bare_held, item_pinch); } else { diff --git a/runtime/tests/vr_hand_tracking_tests.cpp b/runtime/tests/vr_hand_tracking_tests.cpp index 845628d..075a724 100644 --- a/runtime/tests/vr_hand_tracking_tests.cpp +++ b/runtime/tests/vr_hand_tracking_tests.cpp @@ -126,6 +126,11 @@ void TestPinchGate() { Check(gate.Update(true, false, dt), "holding the pinch holds the button"); Check(!gate.Update(false, false, dt), "letting go releases it"); Check(!gate.Update(true, true, dt), "grabbing the wheel stops it"); + + Check(ItemPinch(true, GraspFromJoints(Hand(0.3f, 0.5f, 0.3f))), "a pinch from a relaxed hand is an item"); + Check(!ItemPinch(true, GraspFromJoints(Hand(1.0f, 1.3f, 0.6f))), "a hand closing on the rim is not an item"); + Check(!ItemPinch(true, std::numeric_limits::quiet_NaN()), "an unknown grasp is not an item"); + Check(!ItemPinch(false, 0.0f), "no pinch, no item"); } void TestGestures() {