From 6da6e1690e2e1cadd6a8322b72bc9a2e1ff03d18 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Fri, 23 Sep 2022 13:20:12 +1000 Subject: [PATCH] Add OpenXR palm pose extension support --- modules/openxr/SCsub | 1 + .../openxr/action_map/openxr_action_map.cpp | 22 +++++-- modules/openxr/action_map/openxr_defs.cpp | 20 ++++++ .../extensions/openxr_extension_wrapper.h | 5 ++ .../openxr_htc_vive_tracker_extension.cpp | 9 +++ .../openxr_htc_vive_tracker_extension.h | 2 + .../extensions/openxr_palm_pose_extension.cpp | 66 +++++++++++++++++++ .../extensions/openxr_palm_pose_extension.h | 53 +++++++++++++++ modules/openxr/openxr_api.cpp | 30 +++++++++ modules/openxr/openxr_api.h | 2 + modules/openxr/openxr_interface.cpp | 46 ++++++------- 11 files changed, 228 insertions(+), 28 deletions(-) create mode 100644 modules/openxr/extensions/openxr_palm_pose_extension.cpp create mode 100644 modules/openxr/extensions/openxr_palm_pose_extension.h diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 593d1ff3c1cb..eb9b71d6a35e 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -85,6 +85,7 @@ if env["platform"] == "android": if env["vulkan"]: env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 0eb53024420b..185e44c29d30 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -157,7 +157,11 @@ void OpenXRActionMap::remove_interaction_profile(Ref p } void OpenXRActionMap::create_default_action_sets() { - // Note, if you make changes here make sure to delete your default_action_map.tres file of it will load an old version. + // Note: + // - if you make changes here make sure to delete your default_action_map.tres file of it will load an old version. + // - our palm pose is only available if the relevant extension is supported, + // we still want it to be part of our action map as we may deploy the same game to platforms that do and don't support it. + // - the same applies for interaction profiles that are only supported if the relevant extension is supported. // Create our Godot action set Ref action_set = OpenXRActionSet::new_action_set("godot", "Godot action set"); @@ -200,6 +204,7 @@ void OpenXRActionMap::create_default_action_sets() { "/user/vive_tracker_htcx/role/keyboard"); Ref aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); Ref grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); + Ref palm_pose = action_set->add_new_action("palm_pose", "Palm pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); Ref haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC, "/user/hand/left," "/user/hand/right," @@ -222,6 +227,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click"); profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click"); // generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs @@ -233,6 +239,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click"); profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // wmr controller has no a/b/x/y buttons @@ -253,6 +260,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); // wmr controllers have no select button we can use profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click"); // wmr controller has no a/b/x/y buttons @@ -275,6 +283,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); // touch controllers have no select button we can use profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand @@ -299,6 +308,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); // index controllers have no select button we can use profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers @@ -321,16 +331,12 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic"); add_interaction_profile(profile); - // Note, the following profiles are all part of extensions. - // We include these regardless of whether the extension is active. - // We want our action map to be as complete as possible so our game is as portable as possible. - // It is very possible these will in due time become core. - // Create our HP MR controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller"); profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); // hpmr controllers have no select button we can use profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click"); // hpmr controllers only register click, not touch, on our a/b/x/y buttons @@ -353,6 +359,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); // Odyssey controllers have no select button we can use profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click"); // Odyssey controller has no a/b/x/y buttons @@ -375,6 +382,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click"); profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand @@ -398,6 +406,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click"); profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand @@ -421,6 +430,7 @@ void OpenXRActionMap::create_default_action_sets() { profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); profile->add_new_binding(menu_button, "/user/hand/left/input/home/click,/user/hand/right/input/home/click"); profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click"); diff --git a/modules/openxr/action_map/openxr_defs.cpp b/modules/openxr/action_map/openxr_defs.cpp index 89860199be27..59ce829f1bb9 100644 --- a/modules/openxr/action_map/openxr_defs.cpp +++ b/modules/openxr/action_map/openxr_defs.cpp @@ -62,6 +62,8 @@ OpenXRDefs::IOPath OpenXRDefs::simple_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -78,6 +80,8 @@ OpenXRDefs::IOPath OpenXRDefs::vive_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -109,6 +113,8 @@ OpenXRDefs::IOPath OpenXRDefs::motion_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -143,6 +149,8 @@ OpenXRDefs::IOPath OpenXRDefs::hpmr_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -175,6 +183,8 @@ OpenXRDefs::IOPath OpenXRDefs::touch_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -213,6 +223,8 @@ OpenXRDefs::IOPath OpenXRDefs::index_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -260,6 +272,8 @@ OpenXRDefs::IOPath OpenXRDefs::odyssey_io_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -294,6 +308,8 @@ OpenXRDefs::IOPath OpenXRDefs::vive_cosmos_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -331,6 +347,8 @@ OpenXRDefs::IOPath OpenXRDefs::vive_focus3_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL }, @@ -371,6 +389,8 @@ OpenXRDefs::IOPath OpenXRDefs::huawei_controller_paths[] = { { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, + { "Palm pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/palm_ext/pose", OpenXRAction::OPENXR_ACTION_POSE }, { "Home click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/home/click", OpenXRAction::OPENXR_ACTION_BOOL }, { "Home click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/home/click", OpenXRAction::OPENXR_ACTION_BOOL }, diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index ecc6e0dd4eea..52c84decf885 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -87,6 +87,11 @@ public: return false; } + // Return false if this extension is responsible for this path but the path is not enabled + virtual bool is_path_supported(const String &p_path) { + return true; + } + OpenXRExtensionWrapper(OpenXRAPI *p_openxr_api) { openxr_api = p_openxr_api; }; virtual ~OpenXRExtensionWrapper() = default; }; diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp index 302acf4e3056..88cc7c061c54 100644 --- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp +++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp @@ -65,3 +65,12 @@ bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &eve } break; } } + +bool OpenXRHTCViveTrackerExtension::is_path_supported(const String &p_path) { + if (p_path == "/interaction_profiles/htc/vive_tracker_htcx") { + return available; + } + + // Not a path under this extensions control, so we return true; + return true; +} diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h index 7f37351f2795..ab8e8535f1f4 100644 --- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h +++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h @@ -41,7 +41,9 @@ public: virtual ~OpenXRHTCViveTrackerExtension() override; bool is_available(); + virtual bool on_event_polled(const XrEventDataBuffer &event) override; + virtual bool is_path_supported(const String &p_path) override; private: static OpenXRHTCViveTrackerExtension *singleton; diff --git a/modules/openxr/extensions/openxr_palm_pose_extension.cpp b/modules/openxr/extensions/openxr_palm_pose_extension.cpp new file mode 100644 index 000000000000..fd3b8f50fe34 --- /dev/null +++ b/modules/openxr/extensions/openxr_palm_pose_extension.cpp @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* openxr_palm_pose_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "openxr_palm_pose_extension.h" +#include "core/string/print_string.h" + +OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::singleton = nullptr; + +OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::get_singleton() { + return singleton; +} + +OpenXRPalmPoseExtension::OpenXRPalmPoseExtension(OpenXRAPI *p_openxr_api) : + OpenXRExtensionWrapper(p_openxr_api) { + singleton = this; + + request_extensions[XR_EXT_PALM_POSE_EXTENSION_NAME] = &available; +} + +OpenXRPalmPoseExtension::~OpenXRPalmPoseExtension() { + singleton = nullptr; +} + +bool OpenXRPalmPoseExtension::is_available() { + return available; +} + +bool OpenXRPalmPoseExtension::is_path_supported(const String &p_path) { + if (p_path == "/user/hand/left/input/palm_ext/pose") { + return available; + } + + if (p_path == "/user/hand/right/input/palm_ext/pose") { + return available; + } + + // Not a path under this extensions control, so we return true; + return true; +} diff --git a/modules/openxr/extensions/openxr_palm_pose_extension.h b/modules/openxr/extensions/openxr_palm_pose_extension.h new file mode 100644 index 000000000000..a7ef83c5d510 --- /dev/null +++ b/modules/openxr/extensions/openxr_palm_pose_extension.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* openxr_palm_pose_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OPENXR_PALM_POSE_EXTENSION_H +#define OPENXR_PALM_POSE_EXTENSION_H + +#include "openxr_extension_wrapper.h" + +class OpenXRPalmPoseExtension : public OpenXRExtensionWrapper { +public: + static OpenXRPalmPoseExtension *get_singleton(); + + OpenXRPalmPoseExtension(OpenXRAPI *p_openxr_api); + virtual ~OpenXRPalmPoseExtension() override; + + bool is_available(); + + virtual bool is_path_supported(const String &p_path) override; + +private: + static OpenXRPalmPoseExtension *singleton; + + bool available = false; +}; + +#endif // OPENXR_PALM_POSE_EXTENSION_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 94095eb61cd0..511d111aafb5 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -49,6 +49,7 @@ #endif #include "extensions/openxr_htc_vive_tracker_extension.h" +#include "extensions/openxr_palm_pose_extension.h" #include "modules/openxr/openxr_interface.h" @@ -184,6 +185,20 @@ bool OpenXRAPI::is_extension_supported(const String &p_extension) const { return false; } +bool OpenXRAPI::is_path_supported(const String &p_path) { + // This checks with extensions whether a path is *unsupported* and returns false if this is so. + // This allows us to filter out paths that are only available if related extensions are supported. + // WARNING: This method will return true for unknown/mistyped paths as we have no way to validate those. + + for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { + if (!wrapper->is_path_supported(p_path)) { + return false; + } + } + + return true; +} + void OpenXRAPI::copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len) { CharString char_string = p_string.utf8(); int len = char_string.length(); @@ -1382,6 +1397,10 @@ void OpenXRAPI::pre_render() { // 2) It will use the previous timing to pause our thread so that rendering starts as close to displaying as possible // This must thus be called as close to when we start rendering as possible XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, nullptr }; + frame_state.predictedDisplayTime = 0; + frame_state.predictedDisplayPeriod = 0; + frame_state.shouldRender = false; + XrResult result = xrWaitFrame(session, &frame_wait_info, &frame_state); if (XR_FAILED(result)) { print_line("OpenXR: xrWaitFrame() was not successful [", get_error_string(result), "]"); @@ -1643,6 +1662,7 @@ OpenXRAPI::OpenXRAPI() { #endif // register our other extensions + register_extension_wrapper(memnew(OpenXRPalmPoseExtension(this))); register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this))); } @@ -2063,6 +2083,11 @@ XrPath OpenXRAPI::get_interaction_profile_path(RID p_interaction_profile) { } RID OpenXRAPI::interaction_profile_create(const String p_name) { + if (!is_path_supported(p_name)) { + // The extension enabling this path must not be active, we will silently skip this interaction profile + return RID(); + } + InteractionProfile new_interaction_profile; XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_interaction_profile.path); @@ -2099,6 +2124,11 @@ void OpenXRAPI::interaction_profile_clear_bindings(RID p_interaction_profile) { } bool OpenXRAPI::interaction_profile_add_binding(RID p_interaction_profile, RID p_action, const String p_path) { + if (!is_path_supported(p_path)) { + // The extension enabling this path must not be active, we will silently skip this binding + return false; + } + InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile); ERR_FAIL_NULL_V(ip, false); diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index dc224c423712..e541e921abc5 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -81,6 +81,8 @@ private: bool ext_vive_focus3_available = false; bool ext_huawei_controller_available = false; + bool is_path_supported(const String &p_path); + // composition layer providers Vector composition_layer_providers; diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 6c2f08e21dda..c5eda88db830 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -176,34 +176,36 @@ void OpenXRInterface::_load_action_map() { // Note, we can only have one entry per interaction profile so if it already exists we clear it out RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path()); - openxr_api->interaction_profile_clear_bindings(ip); + if (ip.is_valid()) { + openxr_api->interaction_profile_clear_bindings(ip); - Array xr_bindings = xr_interaction_profile->get_bindings(); - for (int j = 0; j < xr_bindings.size(); j++) { - Ref xr_binding = xr_bindings[j]; - Ref xr_action = xr_binding->get_action(); + Array xr_bindings = xr_interaction_profile->get_bindings(); + for (int j = 0; j < xr_bindings.size(); j++) { + Ref xr_binding = xr_bindings[j]; + Ref xr_action = xr_binding->get_action(); - Action *action = nullptr; - if (xr_actions.has(xr_action)) { - action = xr_actions[xr_action]; - } else { - print_line("Action ", xr_action->get_name(), " isn't part of an action set!"); - continue; + Action *action = nullptr; + if (xr_actions.has(xr_action)) { + action = xr_actions[xr_action]; + } else { + print_line("Action ", xr_action->get_name(), " isn't part of an action set!"); + continue; + } + + PackedStringArray paths = xr_binding->get_paths(); + for (int k = 0; k < paths.size(); k++) { + openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]); + } } - PackedStringArray paths = xr_binding->get_paths(); - for (int k = 0; k < paths.size(); k++) { - openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]); + // Now submit our suggestions + openxr_api->interaction_profile_suggest_bindings(ip); + + // And record it in our array so we can clean it up later on + if (interaction_profiles.has(ip)) { + interaction_profiles.push_back(ip); } } - - // Now submit our suggestions - openxr_api->interaction_profile_suggest_bindings(ip); - - // And record it in our array so we can clean it up later on - if (interaction_profiles.has(ip)) { - interaction_profiles.push_back(ip); - } } } }