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; + } + }