Repository navigation
Conversation
|
Hmmm, I'm not sure if we need all the new trackers here. If the user is holding controllers, say the Sony PSVR2 ones, then yes, those use the This only applies when the user isn't holding controllers, and in that case we can fully mimic the behavior in OpenXR. So instead of introducing With The way this works in OpenXR, and what I think we should mimic here, is that we have a single tracker ( This way game logic can react to the "trigger" signal on the respective hands, use the eye tracker for the ray, and seamlessly switch between a user doing pinch while looking, and using controller while looking, and do so in a way that is platform agnostic. |
|
@BastiaanOlij what you are describing seems to be more inline with other XR platforms, that lean more on VR controllers, indeed. My PR currently tries to stay as close as possible to the low-level visionOS APIs, while still using the XRController3D types. Having the design be closer to visionOS allows games to be more similar to the rest of visionOS; but the downside is that it will be more work to port Godot games from other platforms to visionOS. What do you think of the following solution, that combines the best of both worlds:
? Using "left_hand" and "right_hand"I did that in a previous version, but it caused issues when combining PSVR2 controllers and pinch events in the same game (I couldn't achieve what I'm showing in the video below): Controller.conflict.mp4However, we can probably solve that by having "left_hand" and "right_hand" dynamically switch between the PSVR2 controllers and the "spatial events" when you put the PSVR2 controllers in your hands, like you are suggesting. Eye rays
Indeed, it's the latter: the ray is for both eyes, but one ray is for the left hand and the other ray is for the right hand. I had a previous version where I had a single "user/eyes_ext" controller, that was controlled by the last hand that pinched. I'm ok with either approaches. The main difference with having a single ray is that it requires a bit of state tracking to make sure that both hands are not fighting for the same ray when you pinch with both hands at the same time. Emulating VR controllers with spatial eventsI agree that it would be very useful to emulate VR controllers with the visionOS spatial events. It will make it much easier to port games to visionOS and to re-use UI code such as this: I did that in my sample above, where the emulated controller is the red ray: Next steps for this PRIf you agree, I'm going to update this PR with:
What do you think? |
|
Hey Olivier, I think that is where we differ in stance. I find it far more important to have cross platform synergy, especially if the visionOS behavior can still be achieved by simply knowing where the data comes from. We should only have visionOS specific implementations if there is behavior we can't replicate any other way. So with your 4 conclusions, I'm only 75% on board:
I don't like 4, because it introduces visionOS only behavior that can easily be reproduced in a cross platform way by reacting to the trigger action and querying the |
|
While I broadly agree with Bastiaan, that the For example, in OpenXR, we have the Similarly, in WebXR, there's some odd platform-specific events (for example, the touch events) where we get a 3D position and ray, but it only updates when the event happens. This can't really be mapped to any of our standard trackers, but it's still a decent fit for a tracker (since we get a 3D position and ray), so these are exposed custom trackers. So, I think it'd be fine for visionOS to add some extra trackers for platform-specific stuff, that doesn't map to things from other platforms. |
|
Ok, to summarize the plan (@BastiaanOlij if I understood correctly), we will have the following trackers:
I think that this would work, and provide all the needed data. I should be able to rework my sample above to behave the exact same. And we won't really need the extra trackers, since all the data can be derived from the above (unless you prefer the additional trackers, @dsnopek). My only concern with this plan is that it does not exactly emulate how other VR platforms work: especially the "laser pointer" interaction (from SteamVR or Meta Quest with controllers) where you aim with a laser coming from your VR controllers. With the plan above, if the Godot game is ported unchanged, you would be aiming with your hands instead of your eyes, which will feel strange on visionOS. |
Cherry-picked from godotengine#123732, adapted to this branch: spatial events are converted on UIKit's thread and delivered to the XR interface on the engine thread, the Swift closure captures the renderer weakly, and the right-hand tracker description is corrected. (cherry picked from commit 3802434) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 0849b05)
Thats a mistaken assumption Oliver. Yes, a game that never thought of using eye tracking, and is using hand tracking with lasers, will thus behave the same on AVP, and not to the expectation of AVP devs. But you have to understand that in this scenario, it is very likely the developer has done many more things around the hands, such as actually visualising the laser pointers, and suddenly moving the hand position to where the players head is, will cause a lot of problems. This is a game breaking behavioral change. This is also why I mentioned to have the ray positions as the aim pose, while using hand tracking data to simulate grip and palm poses as there is an expectation that those work as well. Developers who are doing the proper thing, e.g. using the eye tracker with pinch gestures, for them the functionality will be equal and predictable on AVP and other devices. Their game is already designed with the assumption the ray comes from the head, and they do not have the expectation that the position given is near the hand. @dsnopek I agree with you, if we have functionality that does not fit our more generic solution, introducing extra trackers makes sence. I am taking a bit of leeway here because the eye tracker, is not generic but specific to the eye gaze OpenXR extension. But I feel we should make it generic as this functionality is starting to become more prevalant. If we do want to center ray pose available on the hands, in this case I wouldn't go for extra trackers, but just adding extra poses to the left and right hand tracker. This way there is no duplication of inputs, and a developer can easily check if the poses are active and available and switch behavior accordingly. |
|
Also @oPinon , am I correct in understanding that we do not get an eye ray pose when the user is not pinching? That would cause a lot of UI issues as well. A dev will want to highlight the object or UI element the user is about the interact with. I definately want that behavior as a user. I have been playing a lot of Augmental puzzles lately, I can't imagine that game working without being able to know which cell I'm about to interact with. |
Yes, you are correct. The app only gets the eye ray pose during a pinch event. And the eye ray does not move while the user is dragging; it's only at the start of a gesture. There is a way to have hover effects on visionOS, by having the OS do the hit-testing and render the hover effects, rather than the app. The reason why the app does not get the eye ray is to protect the privacy of the user. For CompositorServices apps, it means providing an index texture with all the objects that should have hover effects:
If the app (or Godot game) provides this data, visionOS does the hit-testing and renders the hover effects. When the user pinches, the app gets the ray as well as the I have a prototype of integrating this in Godot: it works, but the main downside is that I modified Godot's main rendering code to optionally write to this new index texture, in the fragment shader. I wonder if this could be done in a more modular way, similar to what the developer of CONNECTOME did in Unity:
|
|
@oPinon hmm, yeah writing to the index buffer I think is going to be an entirely different challenge, especially on a generic engine like Godot that tries to hide the render implementation details away from the user. So there is writing to this buffer, and somehow linking up the values to the actual nodes related to the geometry that the user will understand. I have some ideas about enhancing shaders so they support outputting to additional buffers in a more generic way but there are a lot of challenges in joining all the dots. |
3802434 to
d0d1146
Compare
| // Correcting the transforms from each hand to | ||
| // map to the Godot and OpenXR convention: | ||
| // https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_hand_interaction | ||
| Transform3D transform_correction; |
There was a problem hiding this comment.
The transform of the spatial events did not match the existing controllers or the OpenXR convention. I fixed it by applying a corrective transform to each hand.
Before this fix (you can see that -Z points upwards):

After this fix (note that +Y points upwards and that -Z points forward):
![]() |
![]() |
|---|---|
| Godot spatial events after fix | OpenXR convention |
And here is a video showing the Spatial Events and the PSVR2 controllers side by side: you can see that the orientation now matches (aside from a small angular offset):
Spatial.Events.orientation_hevc.mp4
2a2215a to
3d0e597
Compare
|
@BastiaanOlij I have applied all the changes suggested above. The only part that I'm unsure of is that now, for a game that was made for VR controllers, you have "left_hand" and "right_hand" floating when no VR controller is connected (because it maps to the Spatial Events): Spatial.Events.Controllers_hevc.mp4Maybe I can fix it by adding/removing the controllers from the XRServer every time you start/end a pinch? |
When no pose data is available, you can invalidate the pose, there is a tickbox on the XRController3D node that will only make the node visible, if pose data is available.. That said, this is why I suggested also taking hand tracking data into account when mimicking controllers. I really need to add some more functionality to the hand tracking demo we have, the whole idea here is to have seamless switching between controller tracking and hand tracking. As an application developer, I shouldn't need to worry about where my data is coming from, as long as I can be consistent in making my application work. |
BastiaanOlij
left a comment
There was a problem hiding this comment.
Feels like we're very close to where we want to be.
It does feel like switching between controller tracking and spatial event tracking is a little undefined but it's probably workable. I need to properly test this.
Would be interesting to see how reliable this all works with our hand tracking demo.. I'll spend some more time on this later in the week.
|
|
||
| // Updating the hand pose. | ||
| Transform3D pose = p_event.hand_pose * hand->transform_correction; | ||
| hand->controller->tracker->set_pose("default", pose, Vector3(), Vector3()); |
There was a problem hiding this comment.
This should also set the aim pose, and we should either invalidate the grip and palm poses, or obtain those from hand tracking data.
There was a problem hiding this comment.
I changed it to:
// Updating the hand pose.
Transform3D pose = p_event.hand_pose * hand->transform_correction;
for (const String name : { "default", "aim" }) {
hand->controller->tracker->set_pose(name, pose, Vector3(), Vector3());
}
for (const String name : { "grip", "palm" }) {
hand->controller->tracker->invalidate_pose(name);
}| hand->ray_submitted = false; | ||
| } | ||
|
|
||
| hand->controller->controlled_by_spatial_event = active; |
There was a problem hiding this comment.
If this is not active, should we still be setting poses? Or should they be invalidated? Not sure..
There was a problem hiding this comment.
I updated the code to invalidate the poses when no pinch is active.
// Invalidating poses on controllers when not tracked.
for (const VisionOSSharedController &hand : { left_hand, right_hand }) {
if (hand.source == VisionOSSharedController::Source::None) {
for (const String name : { "default", "aim", "grip", "palm" }) {
hand.tracker->invalidate_pose(name);
}
}
}|
I have used this in a game I shipped to the app store, it made my Vision pickup way better! |
visionOS: update spatial pinch input to the current revision of godotengine#123732
Listening for SpatialEvents from visionOS and converting them to XRController3D on the VisionOSXRInterface.
3d0e597 to
8b2a709
Compare






Feature
This pull request adds support for Spatial Events from visionOS (when you look at your game and pinch and/or drag).
Here is a demo project using the feature in Godot:
spatial-events.zip
Spatial.Events.test_hevc.mp4
The events map to XRController3D nodes:
Pinch events are sent as "trigger_click" actions on "left_hand" and "right_hand".
Ray
The "/user/eyes_ext" starts from your head and goes towards the direction that you are looking at. It only moves during the initial pinch and does not move while you drag. The ray goes towards -Z, relative to the controller.
Pinch
The "left_hand" and "right_hand" follow your hand. You can combine them with the ray above to implement drag gestures.
Compatibility with hand tracking and controller tracking
Note that this is compatible with hand tracking and controller tracking, but does not require them.
This PR shares the same trackers used for PSVR2 controller tracking ("left_hand" and "right_hand"), but dynamically switches between the two during a pinch.
Here is a video showing that controller tracking still works:
Hands.+.controllers.demo_hevc.mp4
Differences with other platforms
This PR tries to map the visionOS API to existing Godot concepts, but it is not a 1-1 match. While visionOS also supports hand tracking and controller tracking, the "spatial events" are the default interaction model, and work a bit differently: the main difference is using eye tracking to target objects in the scene or target the UI, combined with hand tracking to do various gestures.