Skip to content

visionOS: update spatial pinch input to the current revision of godotengine/godot#123732 - #4

Merged
Clancey merged 2 commits into
visionos-4.8from
clancey-visionos-spatial-pinch-update
Oct 6, 2026
Merged

Clancey merged 2 commits into
visionos-4.8from
clancey-visionos-spatial-pinch-update

Conversation

@Clancey

@Clancey Clancey commented Oct 6, 2026

Copy link
Copy Markdown
Owner

What problem(s) does this PR solve?

Additional information

Upstream design now carried

  • Spatial events no longer register their own trackers. The system pinch (look and pinch) drives the shared left_hand / right_hand XRControllerTrackers through VisionOSSharedController, which VisionOSXRInterface owns. Accessories and hand gestures drive the same trackers. While a spatial event is active, it owns trigger_click and the default pose. The pose is corrected to the OpenXR hand interaction convention.
  • The selection ray is published once per pinch as the default pose of a new /user/eyes_ext tracker.
  • left_hand / right_hand are now always registered.

Conflicts and how existing fork behavior was preserved

fdd2c87 was applied after the gesture-derivation and scene-understanding commits, so the delta conflicts with fork code rather than the other way round. Resolution:

  • Hand-derived gestures: VisionOSHandTracking no longer owns its own left_hand / right_hand trackers. publish_gestures writes into the shared controllers instead. While a spatial event is active, the optical pinch does not write trigger_click or the default pose. After the event ends, the optical trigger_click stays released until the optical pinch itself releases, so one pinch cannot click twice. Analog trigger, grip / grip_click, the aim / grip / palm poses and the visionos_hand_tracking profile still come from hand tracking. A hand with an accessory attached is still left to the accessory.
  • Accessories: VisionOSControllerTracking uses the shared trackers. It skips a hand while a spatial event drives it. Haptics lookups are null-safe.
  • Thread safety: the fork's engine-thread dispatch of spatial events and the [weak renderer] capture are kept. on_spatial_event is ignored until the interface is initialized.
  • Lost head tracking: visionos_reset_input_tracking now resets the shared controllers. Its tests were updated.
  • Scene understanding / face tracking / passthrough: these are unchanged.
  • Fixes on top of upstream: eyes_ray.unref() (upstream calls unreference()), Hand::controller defaults to nullptr, null guards, and spatial-event state is reset on uninitialize. The shared trackers are created only after CompositorServices initialization succeeds, so a failed initialize does not leave them registered.

Downstream-visible changes

  • Removed trackers: visionos/left_hand_pinch, visionos/right_hand_pinch, visionos/left_hand_ray, visionos/right_hand_ray.
    • LogicLand Ui/3d/xr_rig.gd (_spatial_pinch, _spatial_ray, _log_spatial_trackers) and tests/xr_rig_test.gd read these and must migrate before taking a release with this change. Otherwise gaze-and-pinch selection gets no ray.
    • Pinch: use left_hand / right_hand trigger_click, plus the default pose while pressed.
    • Ray: use the default pose of /user/eyes_ext. It is a single tracker shared by both hands and is updated once at the start of each pinch.
  • A system pinch can no longer be told apart from an optical or accessory trigger by tracker name.
  • During a system pinch, the default pose of left_hand / right_hand follows the spatial-event hand pose instead of the optical aim pose. Use aim for a stable optical aim.
  • left_hand / right_hand exist even when hand and controller tracking are both disabled. Their descriptions changed to "visionOS left/right controller and spatial event".
  • New VisionOSSharedController. VisionOSHandTracking::initialize lost its accessory flag, and VisionOSHandTracking::uninitialize was removed. VisionOSControllerTracking::initialize and VisionOSSpatialEventTracking::initialize take the shared controllers.

Validation

  • scons platform=visionos target=template_release arch=arm64 dev_mode=yes module_text_server_fb_enabled=yes tests=no debug_symbols=no: passed
  • Same flags with simulator=yes: passed
  • scons platform=macos target=editor arch=arm64 dev_mode=yes module_text_server_fb_enabled=yes tests=yes: passed
  • godot.macos.editor.arm64 --headless --test --test-case="*visionOS*": 56 test cases and 11390 assertions passed. This includes the new spatial-event arbitration test and the updated lost-head-tracking test.
  • godot.macos.editor.arm64 --headless --test --test-case="*XR*,*[XR]*": passed
  • xmllint on VisionOSXRInterface.xml: passed
  • Not run: on-device testing on Apple Vision Pro.

Coordination

  • The unmerged clancey-qr-tracking-native-contract branch changes visionos_xr_interface.{h,mm} and scene understanding. It will likely need a rebase and conflict resolution against this change, mainly in VisionOSXRInterface::initialize / uninitialize.
  • Merging into visionos-4.8 republishes the visionos-latest release that LogicLand CI consumes. Hold the merge until LogicLand has migrated off the removed tracker names.

…engine#123732

Move the spatial event support from the original revision of
godotengine#123732 by Olivier Pinon to its current revision
(3d0e597).

Upstream changes carried over:
- Spatial events no longer register their own trackers. The system pinch
  drives the `left_hand` and `right_hand` controller trackers that
  accessories and hand gestures already use, through the new
  VisionOSSharedController owned by VisionOSXRInterface. While a spatial
  event is active it owns `trigger_click` and the `default` pose
  (corrected to the OpenXR hand interaction convention).
- The selection ray is published once per pinch as the `default` pose of
  a new `/user/eyes_ext` tracker.
- `left_hand` and `right_hand` are always registered.

Adaptations for this fork:
- Hand-gesture mirroring now writes to the shared controllers. While a
  spatial event is active, the optical pinch does not write
  `trigger_click` or the `default` pose. After the event ends, the
  optical `trigger_click` stays released until the optical pinch
  releases, so one pinch cannot click twice. Analog trigger, grip, and
  the aim, grip and palm poses still come from hand tracking.
- Accessory updates skip a hand while a spatial event drives it.
- The engine-thread dispatch of spatial events and the weak renderer
  capture are kept.
- Null guards were added, `eyes_ray` is released with unref(),
  spatial-event state is reset on uninitialize, and events are ignored
  before the interface is initialized.
- The shared trackers are created only after CompositorServices
  initialization succeeds.
- visionos_reset_input_tracking takes the shared controllers. The
  arbitration helpers are header-only and covered by unit tests.

Removed trackers: `visionos/left_hand_pinch`, `visionos/right_hand_pinch`,
`visionos/left_hand_ray` and `visionos/right_hand_ray`.
@Clancey
Clancey marked this pull request as ready for review October 6, 2026 05:08
@Clancey
Clancey merged commit 1b0e7fe into visionos-4.8 Oct 6, 2026
44 checks passed
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.

1 participant