Skip to content

Fix XrNavigation NaN rig corruption on gamepads without thumbstick axes#8850

Merged
mvaligursky merged 1 commit into
mainfrom
mv-xrnav-nan-axes-guard
Jun 5, 2026
Merged

Fix XrNavigation NaN rig corruption on gamepads without thumbstick axes#8850
mvaligursky merged 1 commit into
mainfrom
mv-xrnav-nan-axes-guard

Conversation

@mvaligursky

@mvaligursky mvaligursky commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Entering VR on Apple Vision Pro left the scene blank (only the camera clear color visible) while head tracking worked. The camera rig transform was being corrupted to NaN on the second XR frame.

Cause:
XrNavigation reads thumbstick input from gamepad.axes[2]/[3] (the WebXR xr-standard mapping). AVP hand-tracking input sources expose a gamepad with an empty axes array, so those reads return undefined and -undefined is NaN.

The visible corruption was specific to turnMode: 'smooth' — its guard Math.abs(turn) <= threshold does not catch NaN (NaN comparisons are always false), so the NaN flowed into rotateLocal and corrupted the rig — and its child camera — to NaN permanently. Splats still submitted but projected to NaN, so nothing rasterized.

This was not a problem in the general case: the snap-turn path (Math.abs(rotate) > threshold) and smooth locomotion (length() > threshold) gate on conditions that are correctly false for NaN, so they were already inert with absent axes — they just never moved. Only the smooth-turn polarity let NaN through. Controller-based headsets (Quest) report real axes[2] = 0 and were unaffected regardless of turn mode.

Changes:

  • Skip input sources whose gamepad does not expose thumbstick axes (axes.length < 4) in handleSmoothLocomotion (covers locomotion and both turn modes) and handleSnapVertical.

On hand-tracking-only devices, thumbstick locomotion/turning is now cleanly inert (there is no thumbstick to drive it) rather than corrupting the rig. Devices with conformant xr-standard gamepads report 4 axes and are unchanged.

XrNavigation reads thumbstick input from gamepad.axes[2]/[3] (the WebXR
xr-standard mapping). Apple Vision Pro hand-tracking input sources expose a
gamepad with an empty axes array, so those reads return undefined and
-undefined is NaN. The smooth-turn guard 'Math.abs(turn) <= threshold' does
not catch NaN (NaN comparisons are always false), so the NaN flowed into
rotateLocal and corrupted the camera rig transform to NaN permanently. Splats
still submitted but projected to NaN, leaving the scene blank in VR on AVP
while head tracking worked. Controller headsets (Quest) report real axes and
were unaffected.

Skip input sources whose gamepad does not expose thumbstick axes
(axes.length < 4) in handleSmoothLocomotion and handleSnapVertical.
@mvaligursky mvaligursky force-pushed the mv-xrnav-nan-axes-guard branch from 6a33e1d to 3015d48 Compare June 5, 2026 14:18
@mvaligursky mvaligursky merged commit 367c6a4 into main Jun 5, 2026
8 checks passed
@mvaligursky mvaligursky deleted the mv-xrnav-nan-axes-guard branch June 5, 2026 14:31
mvaligursky added a commit that referenced this pull request Jun 5, 2026
…es (#8850)

XrNavigation reads thumbstick input from gamepad.axes[2]/[3] (the WebXR
xr-standard mapping). Apple Vision Pro hand-tracking input sources expose a
gamepad with an empty axes array, so those reads return undefined and
-undefined is NaN. The smooth-turn guard 'Math.abs(turn) <= threshold' does
not catch NaN (NaN comparisons are always false), so the NaN flowed into
rotateLocal and corrupted the camera rig transform to NaN permanently. Splats
still submitted but projected to NaN, leaving the scene blank in VR on AVP
while head tracking worked. Controller headsets (Quest) report real axes and
were unaffected.

Skip input sources whose gamepad does not expose thumbstick axes
(axes.length < 4) in handleSmoothLocomotion and handleSnapVertical.

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
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.

2 participants