diff --git a/CHANGES.md b/CHANGES.md
index ba10bf73..33f09da0 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,7 @@
- Update the plugin to Godot v2 Android plugin
- Update to the Godot 4.2 Android library
- Add warning when multiple loaders are selected
+- Add configs for the OpenXR Eye gaze interaction extension
## 1.1.0
- Update Meta OpenXR loader to version 54
diff --git a/demo/addons/godotopenxr/.export/globals.gd b/demo/addons/godotopenxr/.export/globals.gd
index eab25ae6..91241046 100644
--- a/demo/addons/godotopenxr/.export/globals.gd
+++ b/demo/addons/godotopenxr/.export/globals.gd
@@ -1,5 +1,6 @@
@tool
+# Set of supported vendors
const META_VENDOR_NAME = "meta"
const PICO_VENDOR_NAME = "pico"
const LYNX_VENDOR_NAME = "lynx"
@@ -11,3 +12,6 @@ const VENDORS_LIST = [
LYNX_VENDOR_NAME,
KHRONOS_VENDOR_NAME,
]
+
+# Set of custom feature tags supported by the plugin
+const EYE_GAZE_INTERACTION_FEATURE = "XR_EXT_eye_gaze_interaction"
diff --git a/demo/addons/godotopenxr/.export/meta/godot_openxr_meta_editor_export_plugin.gd b/demo/addons/godotopenxr/.export/meta/godot_openxr_meta_editor_export_plugin.gd
index b8dc0a97..26aaee00 100644
--- a/demo/addons/godotopenxr/.export/meta/godot_openxr_meta_editor_export_plugin.gd
+++ b/demo/addons/godotopenxr/.export/meta/godot_openxr_meta_editor_export_plugin.gd
@@ -1,6 +1,10 @@
@tool
extends "../godot_openxr_editor_export_plugin.gd"
+const EYE_TRACKING_NONE_VALUE = 0
+const EYE_TRACKING_OPTIONAL_VALUE = 1
+const EYE_TRACKING_REQUIRED_VALUE = 2
+
const PASSTHROUGH_NONE_VALUE = 0
const PASSTHROUGH_OPTIONAL_VALUE = 1
const PASSTHROUGH_REQUIRED_VALUE = 2
@@ -11,7 +15,20 @@ const HAND_TRACKING_REQUIRED_VALUE = 2
const HAND_TRACKING_FREQUENCY_LOW_VALUE = 0
const HAND_TRACKING_FREQUENCY_HIGH_VALUE = 1
-
+
+const EYE_TRACKING_OPTION = {
+ "option": {
+ "name": "meta_xr_features/eye_tracking",
+ "class_name": "",
+ "type": TYPE_INT,
+ "hint": PROPERTY_HINT_ENUM,
+ "hint_string": "None,Optional,Required",
+ "usage": PROPERTY_USAGE_DEFAULT,
+ },
+ "default_value": EYE_TRACKING_NONE_VALUE,
+ "update_visibility": false,
+}
+
const HAND_TRACKING_OPTION = {
"option": {
"name": "meta_xr_features/hand_tracking",
@@ -57,12 +74,27 @@ func _get_export_options(platform) -> Array[Dictionary]:
return []
return [
- _get_vendor_toggle_option(),
+ _get_vendor_toggle_option(),
+ EYE_TRACKING_OPTION,
HAND_TRACKING_OPTION,
HAND_TRACKING_FREQUENCY_OPTION,
PASSTHROUGH_OPTION,
]
-
+
+
+func _get_export_features(platform, debug) -> PackedStringArray:
+ var features = PackedStringArray()
+
+ if not _supports_platform(platform):
+ return features
+
+ # Add the eye tracking feature if necessary
+ var eye_tracking_value = _get_int_option("meta_xr_features/eye_tracking", EYE_TRACKING_NONE_VALUE)
+ if eye_tracking_value > EYE_TRACKING_NONE_VALUE:
+ features.append(globals.EYE_GAZE_INTERACTION_FEATURE)
+
+ return features
+
func _get_export_option_warning(platform, option) -> String:
if not _supports_platform(platform):
@@ -91,6 +123,16 @@ func _get_android_manifest_element_contents(platform, debug) -> String:
var contents = ""
+ # Check for eye tracking
+ var eye_tracking_value = _get_int_option("meta_xr_features/eye_tracking", EYE_TRACKING_NONE_VALUE)
+ if eye_tracking_value > EYE_TRACKING_NONE_VALUE:
+ contents += " \n"
+ if eye_tracking_value == EYE_TRACKING_OPTIONAL_VALUE:
+ contents += " \n"
+ elif eye_tracking_value == EYE_TRACKING_REQUIRED_VALUE:
+ contents += " \n"
+
+
# Check for hand tracking
var hand_tracking_value = _get_int_option("meta_xr_features/hand_tracking", HAND_TRACKING_NONE_VALUE)
if hand_tracking_value > HAND_TRACKING_NONE_VALUE:
diff --git a/demo/export_presets.cfg b/demo/export_presets.cfg
index 843bb388..ac6f04bd 100644
--- a/demo/export_presets.cfg
+++ b/demo/export_presets.cfg
@@ -206,3 +206,4 @@ meta_xr_features/passthrough=1
xr_features/enable_pico_plugin=false
xr_features/enable_lynx_plugin=false
xr_features/enable_khr_plugin=false
+meta_xr_features/eye_tracking=1
diff --git a/demo/main.gd b/demo/main.gd
index c64b3ae2..868f499d 100644
--- a/demo/main.gd
+++ b/demo/main.gd
@@ -1,9 +1,10 @@
extends Node3D
+var xr_interface : XRInterface = null
# Called when the node enters the scene tree for the first time.
func _ready():
- var xr_interface : XRInterface = XRServer.find_interface("OpenXR")
+ xr_interface = XRServer.find_interface("OpenXR")
if xr_interface and xr_interface.is_initialized():
var vp: Viewport = get_viewport()
vp.use_xr = true
diff --git a/demo/main.tscn b/demo/main.tscn
index 477dd650..f0b8b105 100644
--- a/demo/main.tscn
+++ b/demo/main.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=8 format=3 uid="uid://cqsodpswgup8w"]
+[gd_scene load_steps=10 format=3 uid="uid://cqsodpswgup8w"]
[ext_resource type="Script" path="res://main.gd" id="1_fsva1"]
@@ -20,7 +20,14 @@ size = Vector3(0.1, 0.1, 0.1)
[sub_resource type="BoxMesh" id="BoxMesh_ey3x4"]
size = Vector3(0.1, 0.1, 0.1)
+[sub_resource type="SphereMesh" id="SphereMesh_5gcab"]
+radius = 0.025
+height = 0.05
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_k604q"]
+
[sub_resource type="PlaneMesh" id="PlaneMesh_mjcgt"]
+material = SubResource("StandardMaterial3D_k604q")
size = Vector2(10, 10)
[node name="Main" type="Node3D"]
@@ -39,7 +46,7 @@ shadow_enabled = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.352791, 0)
[node name="LeftHand" type="XRController3D" parent="XROrigin3D"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.460909, 0, -0.241118)
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.460909, 0.388594, -0.241118)
tracker = &"left_hand"
pose = &"aim"
@@ -47,11 +54,19 @@ pose = &"aim"
mesh = SubResource("BoxMesh_3kt6b")
[node name="RightHand" type="XRController3D" parent="XROrigin3D"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.478861, 0, -0.241097)
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.478861, 0.468292, -0.241097)
tracker = &"right_hand"
-[node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/RightHand"]
+[node name="RightHandMesh" type="MeshInstance3D" parent="XROrigin3D/RightHand"]
mesh = SubResource("BoxMesh_ey3x4")
+[node name="EyeGaze" type="XRController3D" parent="XROrigin3D"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.977669, 0)
+tracker = &"/user/eyes_ext"
+
+[node name="EyeGazeMesh" type="MeshInstance3D" parent="XROrigin3D/EyeGaze"]
+transform = Transform3D(1, 0, 0, 0, -0.0133513, 0.999911, 0, -0.999911, -0.0133513, 0, 0, -1.18886)
+mesh = SubResource("SphereMesh_5gcab")
+
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("PlaneMesh_mjcgt")
diff --git a/demo/openxr_action_map.tres b/demo/openxr_action_map.tres
index b8c46494..be65c1d9 100644
--- a/demo/openxr_action_map.tres
+++ b/demo/openxr_action_map.tres
@@ -1,4 +1,4 @@
-[gd_resource type="OpenXRActionMap" load_steps=197 format=3 uid="uid://b1wdu77pwks8y"]
+[gd_resource type="OpenXRActionMap" load_steps=199 format=3 uid="uid://b1wdu77pwks8y"]
[sub_resource type="OpenXRAction" id="OpenXRAction_6v1ja"]
resource_name = "trigger"
@@ -115,7 +115,7 @@ toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right")
resource_name = "default_pose"
localized_name = "Default pose"
action_type = 3
-toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/keyboard")
+toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/keyboard", "/user/eyes_ext")
[sub_resource type="OpenXRAction" id="OpenXRAction_1vol5"]
resource_name = "aim_pose"
@@ -455,7 +455,7 @@ action = SubResource("OpenXRAction_0kk6l")
paths = PackedStringArray("/user/hand/left/output/haptic", "/user/hand/right/output/haptic")
[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_ert82"]
-interaction_profile_path = "/interaction_profiles/pico/neo3_controller"
+interaction_profile_path = "/interaction_profiles/bytedance/pico_neo3_controller"
bindings = [SubResource("OpenXRIPBinding_jwljs"), SubResource("OpenXRIPBinding_ajdja"), SubResource("OpenXRIPBinding_tla6d"), SubResource("OpenXRIPBinding_377i1"), SubResource("OpenXRIPBinding_xa4wm"), SubResource("OpenXRIPBinding_7xuhj"), SubResource("OpenXRIPBinding_03t7v"), SubResource("OpenXRIPBinding_b08bq"), SubResource("OpenXRIPBinding_fxhob"), SubResource("OpenXRIPBinding_661oy"), SubResource("OpenXRIPBinding_vsv5g"), SubResource("OpenXRIPBinding_bxu5n"), SubResource("OpenXRIPBinding_xer3x"), SubResource("OpenXRIPBinding_urrdv"), SubResource("OpenXRIPBinding_e1sbn"), SubResource("OpenXRIPBinding_vmo5c"), SubResource("OpenXRIPBinding_lytd7"), SubResource("OpenXRIPBinding_feotl"), SubResource("OpenXRIPBinding_fxu5d")]
[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_w77tt"]
@@ -830,6 +830,14 @@ paths = PackedStringArray("/user/vive_tracker_htcx/role/left_foot/output/haptic"
interaction_profile_path = "/interaction_profiles/htc/vive_tracker_htcx"
bindings = [SubResource("OpenXRIPBinding_vke8d"), SubResource("OpenXRIPBinding_3o4hq")]
+[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_73bb6"]
+action = SubResource("OpenXRAction_vk7pf")
+paths = PackedStringArray("/user/eyes_ext/input/gaze_ext/pose")
+
+[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_07fna"]
+interaction_profile_path = "/interaction_profiles/ext/eye_gaze_interaction"
+bindings = [SubResource("OpenXRIPBinding_73bb6")]
+
[resource]
action_sets = [SubResource("OpenXRActionSet_kd2ms")]
-interaction_profiles = [SubResource("OpenXRInteractionProfile_kitsa"), SubResource("OpenXRInteractionProfile_uoohe"), SubResource("OpenXRInteractionProfile_k2llo"), SubResource("OpenXRInteractionProfile_2masb"), SubResource("OpenXRInteractionProfile_ert82"), SubResource("OpenXRInteractionProfile_aq5p3"), SubResource("OpenXRInteractionProfile_4petf"), SubResource("OpenXRInteractionProfile_sd46l"), SubResource("OpenXRInteractionProfile_prh4s"), SubResource("OpenXRInteractionProfile_kk5vf"), SubResource("OpenXRInteractionProfile_wxdsn")]
+interaction_profiles = [SubResource("OpenXRInteractionProfile_kitsa"), SubResource("OpenXRInteractionProfile_uoohe"), SubResource("OpenXRInteractionProfile_k2llo"), SubResource("OpenXRInteractionProfile_2masb"), SubResource("OpenXRInteractionProfile_ert82"), SubResource("OpenXRInteractionProfile_aq5p3"), SubResource("OpenXRInteractionProfile_4petf"), SubResource("OpenXRInteractionProfile_sd46l"), SubResource("OpenXRInteractionProfile_prh4s"), SubResource("OpenXRInteractionProfile_kk5vf"), SubResource("OpenXRInteractionProfile_wxdsn"), SubResource("OpenXRInteractionProfile_07fna")]
diff --git a/godotopenxrmeta/src/main/java/org/godotengine/openxrloaders/meta/GodotOpenXRMeta.java b/godotopenxrmeta/src/main/java/org/godotengine/openxrloaders/meta/GodotOpenXRMeta.java
index 3f942477..78e67631 100644
--- a/godotopenxrmeta/src/main/java/org/godotengine/openxrloaders/meta/GodotOpenXRMeta.java
+++ b/godotopenxrmeta/src/main/java/org/godotengine/openxrloaders/meta/GodotOpenXRMeta.java
@@ -17,4 +17,19 @@ public String getPluginName() {
return "GodotOpenXRMeta";
}
+ @Override
+ public boolean supportsFeature(String featureTag) {
+ if ("PERMISSION_XR_EXT_eye_gaze_interaction".equals(featureTag)) {
+ String[] grantedPermissions = getGodot().getGrantedPermissions();
+ if (grantedPermissions != null) {
+ for (String permission : grantedPermissions) {
+ if ("com.oculus.permission.EYE_TRACKING".equals(permission)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
}