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 <noreply@anthropic.com>
This commit is contained in:
iChris4andClaude Opus 5.5 committed 2026-09-25 00:42:48 +02:00
1 parent d3ad9fbd56
commit 38b7f89d74
4 files changed
+19 -4

No files matched your search

+3 -2
View File
@@ -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
@@ -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
+2 -2
View File
@@ -992,8 +992,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
if (bare_held[0] || bare_held[1]) {
std::array<bool, kHands> 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 {
+5
View File
@@ -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<float>::quiet_NaN()), "an unknown grasp is not an item");
Check(!ItemPinch(false, 0.0f), "no pinch, no item");
}
void TestGestures() {