diff options
Diffstat (limited to 'modules')
24 files changed, 849 insertions, 109 deletions
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index afb59b486c..e0deea1106 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -39,10 +39,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l Type next_type = NONE; Type current_type = NONE; - Type previous_type = NONE; + Type prev_type = NONE; - String previous_text = ""; - int previous_column = 0; + String prev_text = ""; + int prev_column = 0; bool prev_is_char = false; bool prev_is_digit = false; @@ -224,9 +224,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } } - previous_type = REGION; - previous_text = ""; - previous_column = j; + prev_type = REGION; + prev_text = ""; + prev_column = j; j = from + (end_key_length - 1); if (region_end_index == -1) { color_region_cache[p_line] = in_region; @@ -243,17 +243,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l // A bit of a hack, but couldn't come up with anything better. if (j > 0 && (str[j] == '&' || str[j] == '^' || str[j] == '%' || str[j] == '+' || str[j] == '-' || str[j] == '~' || str[j] == '.')) { - if (!keywords.has(previous_text)) { - if (previous_text == "PI" || previous_text == "TAU" || previous_text == "INF" || previous_text == "NAN") { + if (prev_text == "true" || prev_text == "false" || prev_text == "self" || prev_text == "null" || prev_text == "PI" || prev_text == "TAU" || prev_text == "INF" || prev_text == "NAN") { + is_binary_op = true; + } else if (!keywords.has(prev_text)) { + int k = j - 1; + while (k > 0 && is_whitespace(str[k])) { + k--; + } + if (!is_symbol(str[k]) || str[k] == '"' || str[k] == '\'' || str[k] == ')' || str[k] == ']' || str[k] == '}') { is_binary_op = true; - } else { - int k = j - 1; - while (k > 0 && is_whitespace(str[k])) { - k--; - } - if (!is_symbol(str[k]) || str[k] == '"' || str[k] == '\'' || str[k] == ')' || str[k] == ']' || str[k] == '}') { - is_binary_op = true; - } } } } @@ -355,7 +353,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } if (!in_function_name && in_word && !in_keyword) { - if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::SIGNAL)) { + if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::SIGNAL)) { in_signal_declaration = true; } else { int k = j; @@ -370,12 +368,12 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l if (str[k] == '(') { in_function_name = true; - } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) { + } else if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) { in_variable_declaration = true; } // Check for lambda. - if (in_function_name && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) { + if (in_function_name && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) { k = j - 1; while (k > 0 && is_whitespace(str[k])) { k--; @@ -491,7 +489,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l } else if (in_function_name) { next_type = FUNCTION; - if (!in_lambda && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) { + if (!in_lambda && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) { color = function_definition_color; } else { color = function_color; @@ -513,20 +511,20 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l if (current_type == NONE) { current_type = next_type; } else { - previous_type = current_type; + prev_type = current_type; current_type = next_type; // no need to store regions... - if (previous_type == REGION) { - previous_text = ""; - previous_column = j; + if (prev_type == REGION) { + prev_text = ""; + prev_column = j; } else { - String text = str.substr(previous_column, j - previous_column).strip_edges(); - previous_column = j; + String text = str.substr(prev_column, j - prev_column).strip_edges(); + prev_column = j; // ignore if just whitespace if (!text.is_empty()) { - previous_text = text; + prev_text = text; } } } diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 98bcdea8f4..acb92b25e1 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -104,7 +104,7 @@ float AudioStreamPlaybackMP3::get_stream_sampling_rate() { return mp3_stream->sample_rate; } -void AudioStreamPlaybackMP3::start(float p_from_pos) { +void AudioStreamPlaybackMP3::start(double p_from_pos) { active = true; seek(p_from_pos); loops = 0; @@ -123,11 +123,11 @@ int AudioStreamPlaybackMP3::get_loop_count() const { return loops; } -float AudioStreamPlaybackMP3::get_playback_position() const { - return float(frames_mixed) / mp3_stream->sample_rate; +double AudioStreamPlaybackMP3::get_playback_position() const { + return double(frames_mixed) / mp3_stream->sample_rate; } -void AudioStreamPlaybackMP3::seek(float p_time) { +void AudioStreamPlaybackMP3::seek(double p_time) { if (!active) { return; } @@ -217,15 +217,15 @@ bool AudioStreamMP3::has_loop() const { return loop; } -void AudioStreamMP3::set_loop_offset(float p_seconds) { +void AudioStreamMP3::set_loop_offset(double p_seconds) { loop_offset = p_seconds; } -float AudioStreamMP3::get_loop_offset() const { +double AudioStreamMP3::get_loop_offset() const { return loop_offset; } -float AudioStreamMP3::get_length() const { +double AudioStreamMP3::get_length() const { return length; } diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index 428ac1240e..c34c1dc5fa 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -61,14 +61,14 @@ protected: virtual float get_stream_sampling_rate() override; public: - virtual void start(float p_from_pos = 0.0) override; + virtual void start(double p_from_pos = 0.0) override; virtual void stop() override; virtual bool is_playing() const override; virtual int get_loop_count() const override; //times it looped - virtual float get_playback_position() const override; - virtual void seek(float p_time) override; + virtual double get_playback_position() const override; + virtual void seek(double p_time) override; virtual void tag_used_streams() override; @@ -104,8 +104,8 @@ public: void set_loop(bool p_enable); virtual bool has_loop() const override; - void set_loop_offset(float p_seconds); - float get_loop_offset() const; + void set_loop_offset(double p_seconds); + double get_loop_offset() const; void set_bpm(double p_bpm); virtual double get_bpm() const override; @@ -122,7 +122,7 @@ public: void set_data(const Vector<uint8_t> &p_data); Vector<uint8_t> get_data() const; - virtual float get_length() const override; + virtual double get_length() const override; virtual bool is_monophonic() const override; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 100db9bc82..394c32f20d 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -140,20 +140,17 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p // This is an implementation of the A* algorithm. int least_cost_id = 0; + int prev_least_cost_id = -1; bool found_route = false; const gd::Polygon *reachable_end = nullptr; float reachable_d = 1e30; bool is_reachable = true; - gd::NavigationPoly *prev_least_cost_poly = nullptr; - while (true) { // Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance. for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) { - gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id]; - - const gd::Edge &edge = least_cost_poly->poly->edges[i]; + const gd::Edge &edge = navigation_polys[least_cost_id].poly->edges[i]; // Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon. for (int connection_index = 0; connection_index < edge.connections.size(); connection_index++) { @@ -164,17 +161,18 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p continue; } + const gd::NavigationPoly &least_cost_poly = navigation_polys[least_cost_id]; float poly_enter_cost = 0.0; - float poly_travel_cost = least_cost_poly->poly->owner->get_travel_cost(); + float poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost(); - if (prev_least_cost_poly != nullptr && (prev_least_cost_poly->poly->owner->get_self() != least_cost_poly->poly->owner->get_self())) { - poly_enter_cost = least_cost_poly->poly->owner->get_enter_cost(); + if (prev_least_cost_id != -1 && (navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self())) { + poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost(); } - prev_least_cost_poly = least_cost_poly; + prev_least_cost_id = least_cost_id; Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end }; - const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway); - const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * poly_travel_cost) + poly_enter_cost + least_cost_poly->traveled_distance; + const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, pathway); + const float new_distance = (least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost) + poly_enter_cost + least_cost_poly.traveled_distance; int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon)); diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 11cc525484..c8e9b9a11e 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -91,8 +91,10 @@ 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_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.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 0eb5302442..185e44c29d 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<OpenXRInteractionProfile> 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<OpenXRActionSet> 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<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); + Ref<OpenXRAction> palm_pose = action_set->add_new_action("palm_pose", "Palm pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> 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 89860199be..59ce829f1b 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_composition_layer_provider.h b/modules/openxr/extensions/openxr_composition_layer_provider.h index 019dffa2a8..ba51389f7d 100644 --- a/modules/openxr/extensions/openxr_composition_layer_provider.h +++ b/modules/openxr/extensions/openxr_composition_layer_provider.h @@ -36,10 +36,9 @@ // Interface for OpenXR extensions that provide a composition layer. class OpenXRCompositionLayerProvider { public: - // TODO changed to normal method definition for now - // CI complains until we implement this, haven't ported it yet from plugin - // virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0; - XrCompositionLayerBaseHeader *get_composition_layer() { return nullptr; }; + virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0; + + virtual ~OpenXRCompositionLayerProvider() {} }; #endif // OPENXR_COMPOSITION_LAYER_PROVIDER_H diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 0ed0155fc7..623c264e6e 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -88,6 +88,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_fb_passthrough_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp new file mode 100644 index 0000000000..259b1236a3 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp @@ -0,0 +1,234 @@ +/*************************************************************************/ +/* openxr_fb_passthrough_extension_wrapper.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_fb_passthrough_extension_wrapper.h" + +#include "core/os/os.h" +#include "scene/main/viewport.h" +#include "scene/main/window.h" + +using namespace godot; + +OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr; + +OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() { + return singleton; +} + +OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper(OpenXRAPI *p_openxr_api) : + OpenXRExtensionWrapper(p_openxr_api) { + request_extensions[XR_FB_PASSTHROUGH_EXTENSION_NAME] = &fb_passthrough_ext; + request_extensions[XR_FB_TRIANGLE_MESH_EXTENSION_NAME] = &fb_triangle_mesh_ext; + singleton = this; +} + +OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() { + cleanup(); +} + +void OpenXRFbPassthroughExtensionWrapper::cleanup() { + fb_passthrough_ext = false; + fb_triangle_mesh_ext = false; +} + +Viewport *OpenXRFbPassthroughExtensionWrapper::get_main_viewport() { + MainLoop *main_loop = OS::get_singleton()->get_main_loop(); + if (!main_loop) { + print_error("Unable to retrieve main loop"); + return nullptr; + } + + auto *scene_tree = Object::cast_to<SceneTree>(main_loop); + if (!scene_tree) { + print_error("Unable to retrieve scene tree"); + return nullptr; + } + + Viewport *viewport = scene_tree->get_root()->get_viewport(); + return viewport; +} + +void OpenXRFbPassthroughExtensionWrapper::on_instance_created(const XrInstance instance) { + if (fb_passthrough_ext) { + bool result = initialize_fb_passthrough_extension(instance); + if (!result) { + print_error("Failed to initialize fb_passthrough extension"); + fb_passthrough_ext = false; + } + } + + if (fb_triangle_mesh_ext) { + bool result = initialize_fb_triangle_mesh_extension(instance); + if (!result) { + print_error("Failed to initialize fb_triangle_mesh extension"); + fb_triangle_mesh_ext = false; + } + } + + if (fb_passthrough_ext) { + openxr_api->register_composition_layer_provider(this); + } +} + +bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() { + return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE; +} + +bool OpenXRFbPassthroughExtensionWrapper::is_composition_passthrough_layer_ready() { + return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && composition_passthrough_layer.layerHandle != XR_NULL_HANDLE; +} + +bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() { + if (passthrough_handle == XR_NULL_HANDLE) { + return false; + } + + if (is_passthrough_enabled()) { + return true; + } + + // Start the passthrough feature + XrResult result = xrPassthroughStartFB(passthrough_handle); + if (!is_valid_passthrough_result(result, "Failed to start passthrough")) { + stop_passthrough(); + return false; + } + + // Create the passthrough layer + result = xrCreatePassthroughLayerFB(openxr_api->get_session(), &passthrough_layer_config, &passthrough_layer); + if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) { + stop_passthrough(); + return false; + } + + // Check if the the viewport has transparent background + Viewport *viewport = get_main_viewport(); + if (viewport && !viewport->has_transparent_background()) { + print_error("Main viewport doesn't have transparent background! Passthrough may not properly render."); + } + + composition_passthrough_layer.layerHandle = passthrough_layer; + + return true; +} + +void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) { + if (fb_passthrough_ext) { + // Create the passthrough feature and start it. + XrResult result = xrCreatePassthroughFB(openxr_api->get_session(), &passthrough_create_info, &passthrough_handle); + if (!openxr_api->xr_result(result, "Failed to create passthrough")) { + passthrough_handle = XR_NULL_HANDLE; + return; + } + } +} + +XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() { + if (is_composition_passthrough_layer_ready()) { + return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer; + } else { + return nullptr; + } +} + +void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() { + if (!fb_passthrough_ext) { + return; + } + + composition_passthrough_layer.layerHandle = XR_NULL_HANDLE; + + XrResult result; + if (passthrough_layer != XR_NULL_HANDLE) { + // Destroy the layer + result = xrDestroyPassthroughLayerFB(passthrough_layer); + openxr_api->xr_result(result, "Unable to destroy passthrough layer"); + passthrough_layer = XR_NULL_HANDLE; + } + + if (passthrough_handle != XR_NULL_HANDLE) { + result = xrPassthroughPauseFB(passthrough_handle); + openxr_api->xr_result(result, "Unable to stop passthrough feature"); + } +} + +void OpenXRFbPassthroughExtensionWrapper::on_session_destroyed() { + if (fb_passthrough_ext) { + stop_passthrough(); + + XrResult result; + if (passthrough_handle != XR_NULL_HANDLE) { + result = xrDestroyPassthroughFB(passthrough_handle); + openxr_api->xr_result(result, "Unable to destroy passthrough feature"); + passthrough_handle = XR_NULL_HANDLE; + } + } +} + +void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() { + if (fb_passthrough_ext) { + openxr_api->unregister_composition_layer_provider(this); + } + cleanup(); +} + +bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_passthrough_extension(const XrInstance p_instance) { + ERR_FAIL_NULL_V(openxr_api, false); + + EXT_INIT_XR_FUNC_V(xrCreatePassthroughFB); + EXT_INIT_XR_FUNC_V(xrDestroyPassthroughFB); + EXT_INIT_XR_FUNC_V(xrPassthroughStartFB); + EXT_INIT_XR_FUNC_V(xrPassthroughPauseFB); + EXT_INIT_XR_FUNC_V(xrCreatePassthroughLayerFB); + EXT_INIT_XR_FUNC_V(xrDestroyPassthroughLayerFB); + EXT_INIT_XR_FUNC_V(xrPassthroughLayerPauseFB); + EXT_INIT_XR_FUNC_V(xrPassthroughLayerResumeFB); + EXT_INIT_XR_FUNC_V(xrPassthroughLayerSetStyleFB); + EXT_INIT_XR_FUNC_V(xrCreateGeometryInstanceFB); + EXT_INIT_XR_FUNC_V(xrDestroyGeometryInstanceFB); + EXT_INIT_XR_FUNC_V(xrGeometryInstanceSetTransformFB); + + return true; +} + +bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_triangle_mesh_extension(const XrInstance p_instance) { + ERR_FAIL_NULL_V(openxr_api, false); + + EXT_INIT_XR_FUNC_V(xrCreateTriangleMeshFB); + EXT_INIT_XR_FUNC_V(xrDestroyTriangleMeshFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshGetVertexBufferFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshGetIndexBufferFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginUpdateFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshEndUpdateFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginVertexBufferUpdateFB); + EXT_INIT_XR_FUNC_V(xrTriangleMeshEndVertexBufferUpdateFB); + + return true; +} diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h new file mode 100644 index 0000000000..1959f3fdc4 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h @@ -0,0 +1,254 @@ +/*************************************************************************/ +/* openxr_fb_passthrough_extension_wrapper.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_FB_PASSTHROUGH_EXTENSION_WRAPPER_H +#define OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H + +#include "../openxr_api.h" +#include "../util.h" + +#include "openxr_composition_layer_provider.h" +#include "openxr_extension_wrapper.h" + +#include <map> + +class Viewport; + +// Wrapper for the set of Facebook XR passthrough extensions. +class OpenXRFbPassthroughExtensionWrapper : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider { + friend class OpenXRAPI; + +public: + void on_instance_created(const XrInstance instance) override; + + void on_session_created(const XrSession session) override; + + void on_session_destroyed() override; + + void on_instance_destroyed() override; + + XrCompositionLayerBaseHeader *get_composition_layer() override; + + bool is_passthrough_supported() { + return fb_passthrough_ext; + } + + bool is_passthrough_enabled(); + + bool start_passthrough(); + + void stop_passthrough(); + + static OpenXRFbPassthroughExtensionWrapper *get_singleton(); + +protected: + OpenXRFbPassthroughExtensionWrapper(OpenXRAPI *p_openxr_api); + ~OpenXRFbPassthroughExtensionWrapper(); + +private: + // Create a passthrough feature + EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughFB, + (XrSession), session, + (const XrPassthroughCreateInfoFB *), create_info, + (XrPassthroughFB *), feature_out) + + // Destroy a previously created passthrough feature + EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughFB, (XrPassthroughFB), feature) + + //*** Passthrough feature state management functions ********* + // Start the passthrough feature + EXT_PROTO_XRRESULT_FUNC1(xrPassthroughStartFB, (XrPassthroughFB), passthrough) + // Pause the passthrough feature + EXT_PROTO_XRRESULT_FUNC1(xrPassthroughPauseFB, (XrPassthroughFB), passthrough) + + EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughLayerFB, (XrSession), session, + (const XrPassthroughLayerCreateInfoFB *), config, + (XrPassthroughLayerFB *), layer_out) + + EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughLayerFB, (XrPassthroughLayerFB), layer) + + EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerPauseFB, (XrPassthroughLayerFB), layer) + EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerResumeFB, (XrPassthroughLayerFB), layer) + + // Set the style of an existing passthrough layer. If the enabled feature set + // doesn’t change, this is a lightweight operation that can be called in every + // frame to animate the style. Changes that may incur a bigger cost: + // - Enabling/disabling the color mapping, or changing the type of mapping + // (monochromatic to RGBA or back). + // - Changing `textureOpacityFactor` from 0 to non-zero or vice versa + // - Changing `edgeColor[3]` from 0 to non-zero or vice versa + // NOTE: For XR_FB_passthrough, all color values are treated as linear. + EXT_PROTO_XRRESULT_FUNC2(xrPassthroughLayerSetStyleFB, + (XrPassthroughLayerFB), layer, + (const XrPassthroughStyleFB *), style) + + // Create a geometry instance to be used as a projection surface for passthrough. + // A geometry instance assigns a triangle mesh as part of the specified layer's + // projection surface. + // The operation is only valid if the passthrough layer's purpose has been set to + // `XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB`. Otherwise, the call this function will + // result in an error. In the specified layer, Passthrough will be visible where the view + // is covered by the user-specified geometries. + // + // A triangle mesh object can be instantiated multiple times - in the same or different layers' + // projection surface. Each instantiation has its own transformation, which + // can be updated using `xrGeometryInstanceSetTransformFB`. + EXT_PROTO_XRRESULT_FUNC3(xrCreateGeometryInstanceFB, + (XrSession), session, + (const XrGeometryInstanceCreateInfoFB *), create_info, + (XrGeometryInstanceFB *), out_geometry_instance) + + // Destroys a previously created geometry instance from passthrough rendering. + // This removes the geometry instance from passthrough rendering. + // The operation has no effect on other instances or the underlying mesh. + EXT_PROTO_XRRESULT_FUNC1(xrDestroyGeometryInstanceFB, (XrGeometryInstanceFB), instance) + + // Update the transformation of a passthrough geometry instance. + EXT_PROTO_XRRESULT_FUNC2(xrGeometryInstanceSetTransformFB, + (XrGeometryInstanceFB), instance, + (const XrGeometryInstanceTransformFB *), transformation) + + // Create a triangle mesh geometry object. + // Depending on the behavior flags, the mesh could be created immutable (data is assigned + // at creation and cannot be changed) or mutable (the mesh is created empty and can be updated + // by calling begin/end update functions). + EXT_PROTO_XRRESULT_FUNC3(xrCreateTriangleMeshFB, + (XrSession), session, + (const XrTriangleMeshCreateInfoFB *), create_info, + (XrTriangleMeshFB *), out_triangle_mesh) + + // Destroy an `XrTriangleMeshFB` object along with its data. The mesh buffers must not be + // accessed anymore after their parent mesh object has been destroyed. + EXT_PROTO_XRRESULT_FUNC1(xrDestroyTriangleMeshFB, (XrTriangleMeshFB), mesh) + + // Retrieve a pointer to the vertex buffer. The vertex buffer is structured as an array of 3 floats + // per vertex representing x, y, and z: `[x0, y0, z0, x1, y1, z1, ...]`. The size of the buffer is + // `maxVertexCount * 3` floats. The application must call `xrTriangleMeshBeginUpdateFB` or + // `xrTriangleMeshBeginVertexBufferUpdateFB` before making modifications to the vertex + // buffer. The buffer location is guaranteed to remain constant over the lifecycle of the mesh + // object. + EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetVertexBufferFB, + (XrTriangleMeshFB), mesh, + (XrVector3f **), out_vertex_buffer) + + // Retrieve the index buffer that defines the topology of the triangle mesh. Each triplet of + // consecutive elements point to three vertices in the vertex buffer and thus form a triangle. The + // size of each element is `indexElementSize` bytes, and thus the size of the buffer is + // `maxTriangleCount * 3 * indexElementSize` bytes. The application must call + // `xrTriangleMeshBeginUpdateFB` before making modifications to the index buffer. The buffer + // location is guaranteed to remain constant over the lifecycle of the mesh object. + EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetIndexBufferFB, + (XrTriangleMeshFB), mesh, + (uint32_t **), out_index_buffer) + + // Begin updating the mesh buffer data. The application must call this function before it makes any + // modifications to the buffers retrieved by `xrTriangleMeshGetVertexBufferFB` and + // `xrTriangleMeshGetIndexBufferFB`. If only the vertex buffer needs to be updated, + // `xrTriangleMeshBeginVertexBufferUpdateFB` can be used instead. To commit the + // modifications, the application must call `xrTriangleMeshEndUpdateFB`. + EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshBeginUpdateFB, (XrTriangleMeshFB), mesh) + + // Signal the API that the application has finished updating the mesh buffers after a call to + // `xrTriangleMeshBeginUpdateFB`. `vertexCount` and `triangleCount` specify the actual + // number of primitives that make up the mesh after the update. They must be larger than zero but + // smaller or equal to the maximum counts defined at create time. Buffer data beyond these counts + // is ignored. + EXT_PROTO_XRRESULT_FUNC3(xrTriangleMeshEndUpdateFB, + (XrTriangleMeshFB), mesh, + (uint32_t), vertexCount, + (uint32_t), triangle_count) + + // Update the vertex positions of a triangle mesh. Can only be called once the mesh topology has + // been set using `xrTriangleMeshBeginUpdateFB`/`xrTriangleMeshEndUpdateFB`. The + // vertex count is defined by the last invocation to `xrTriangleMeshEndUpdateFB`. Once the + // modification is done, `xrTriangleMeshEndVertexBufferUpdateFB` must be called. + EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshBeginVertexBufferUpdateFB, + (XrTriangleMeshFB), mesh, + (uint32_t *), out_vertex_count) + + // Signal the API that the contents of the vertex buffer data has been updated + // after a call to `xrTriangleMeshBeginVertexBufferUpdateFB`. + EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshEndVertexBufferUpdateFB, (XrTriangleMeshFB), mesh) + + bool initialize_fb_passthrough_extension(const XrInstance instance); + + bool initialize_fb_triangle_mesh_extension(const XrInstance instance); + + void cleanup(); + + // TODO: Temporary workaround (https://github.com/GodotVR/godot_openxr/issues/138) + // Address a bug in the passthrough api where XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB is + // returned even when the operation is valid on Meta Quest devices. + // The issue should be addressed on that platform in OS release v37. + inline bool is_valid_passthrough_result(XrResult result, const char *format) { + return openxr_api->xr_result(result, format) || result == XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB; + } + + Viewport *get_main_viewport(); + + bool is_composition_passthrough_layer_ready(); + + static OpenXRFbPassthroughExtensionWrapper *singleton; + + bool fb_passthrough_ext = false; // required for any passthrough functionality + bool fb_triangle_mesh_ext = false; // only use for projected passthrough + + XrPassthroughCreateInfoFB passthrough_create_info = { + XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, + nullptr, + 0, + }; + XrPassthroughFB passthrough_handle = XR_NULL_HANDLE; + + XrPassthroughLayerCreateInfoFB passthrough_layer_config = { + XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, + nullptr, + passthrough_handle, + XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB, + XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, + }; + XrPassthroughStyleFB passthrough_layer_style = { + XR_TYPE_PASSTHROUGH_STYLE_FB, + nullptr, + 1, + { 0, 0, 0, 0 }, + }; + XrPassthroughLayerFB passthrough_layer = XR_NULL_HANDLE; + + XrCompositionLayerPassthroughFB composition_passthrough_layer = { + XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB, + nullptr, + XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, + XR_NULL_HANDLE, + XR_NULL_HANDLE, + }; +}; + +#endif // OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp index 302acf4e30..88cc7c061c 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 7f37351f27..ab8e8535f1 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 0000000000..fd3b8f50fe --- /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 0000000000..a7ef83c5d5 --- /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 08b76ddc6b..16879ac4e5 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -49,8 +49,10 @@ #include "extensions/openxr_vulkan_extension.h" #endif +#include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_hand_tracking_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" +#include "extensions/openxr_palm_pose_extension.h" #include "modules/openxr/openxr_interface.h" @@ -186,6 +188,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(); @@ -1481,6 +1497,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), "]"); @@ -1742,8 +1762,10 @@ OpenXRAPI::OpenXRAPI() { #endif // register our other extensions + register_extension_wrapper(memnew(OpenXRPalmPoseExtension(this))); register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this))); register_extension_wrapper(memnew(OpenXRHandTrackingExtension(this))); + register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper(this))); } OpenXRAPI::~OpenXRAPI() { @@ -1852,6 +1874,18 @@ void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_l } } +bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const { + if (XR_SUCCEEDED(result)) + return true; + + char resultString[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, resultString); + + print_error(String("OpenXR ") + String(format).format(args) + String(" [") + String(resultString) + String("]")); + + return false; +} + RID OpenXRAPI::get_tracker_rid(XrPath p_path) { List<RID> current; tracker_owner.get_owned_list(¤t); @@ -2170,6 +2204,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); @@ -2206,6 +2245,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); @@ -2533,3 +2577,11 @@ bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_tracker, float p_freque return true; } + +void OpenXRAPI::register_composition_layer_provider(OpenXRCompositionLayerProvider *provider) { + composition_layer_providers.append(provider); +} + +void OpenXRAPI::unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider) { + composition_layer_providers.erase(provider); +} diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index fd32c2633e..316886239e 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -36,6 +36,7 @@ #include "core/math/transform_3d.h" #include "core/math/vector2.h" #include "core/os/memory.h" +#include "core/string/print_string.h" #include "core/string/ustring.h" #include "core/templates/rb_map.h" #include "core/templates/rid_owner.h" @@ -83,6 +84,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<OpenXRCompositionLayerProvider *> composition_layer_providers; @@ -285,6 +288,8 @@ public: XRPose::TrackingConfidence transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform); void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity); + bool xr_result(XrResult result, const char *format, Array args = Array()) const; + static bool openxr_is_enabled(bool p_check_run_in_editor = true); static OpenXRAPI *get_singleton(); @@ -347,6 +352,9 @@ public: XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity); bool trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns); + void register_composition_layer_provider(OpenXRCompositionLayerProvider *provider); + void unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider); + OpenXRAPI(); ~OpenXRAPI(); }; diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 6c2f08e21d..01e148e00f 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -176,33 +176,35 @@ 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); - - Array xr_bindings = xr_interaction_profile->get_bindings(); - for (int j = 0; j < xr_bindings.size(); j++) { - Ref<OpenXRIPBinding> xr_binding = xr_bindings[j]; - Ref<OpenXRAction> 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; - } + 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<OpenXRIPBinding> xr_binding = xr_bindings[j]; + Ref<OpenXRAction> 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; + } - 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); + // 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); + // And record it in our array so we can clean it up later on + if (interaction_profiles.has(ip)) { + interaction_profiles.push_back(ip); + } } } } @@ -744,6 +746,24 @@ void OpenXRInterface::end_frame() { } } +bool OpenXRInterface::is_passthrough_supported() { + return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_supported(); +} + +bool OpenXRInterface::is_passthrough_enabled() { + return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_enabled(); +} + +bool OpenXRInterface::start_passthrough() { + return passthrough_wrapper != nullptr && passthrough_wrapper->start_passthrough(); +} + +void OpenXRInterface::stop_passthrough() { + if (passthrough_wrapper) { + passthrough_wrapper->stop_passthrough(); + } +} + void OpenXRInterface::on_state_ready() { emit_signal(SNAME("session_begun")); } @@ -774,6 +794,8 @@ OpenXRInterface::OpenXRInterface() { _set_default_pos(head_transform, 1.0, 0); _set_default_pos(transform_for_view[0], 1.0, 1); _set_default_pos(transform_for_view[1], 1.0, 2); + + passthrough_wrapper = OpenXRFbPassthroughExtensionWrapper::get_singleton(); } OpenXRInterface::~OpenXRInterface() { diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index a99012fd1d..3765f18637 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -37,6 +37,8 @@ #include "action_map/openxr_action_map.h" #include "openxr_api.h" +#include "extensions/openxr_fb_passthrough_extension_wrapper.h" + // declare some default strings #define INTERACTION_PROFILE_NONE "/interaction_profiles/none" @@ -47,6 +49,7 @@ private: OpenXRAPI *openxr_api = nullptr; bool initialized = false; XRInterface::TrackingStatus tracking_state; + OpenXRFbPassthroughExtensionWrapper *passthrough_wrapper = nullptr; // At a minimum we need a tracker for our head Ref<XRPositionalTracker> head; @@ -129,6 +132,11 @@ public: virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void end_frame() override; + virtual bool is_passthrough_supported() override; + virtual bool is_passthrough_enabled() override; + virtual bool start_passthrough() override; + virtual void stop_passthrough() override; + void on_state_ready(); void on_state_visible(); void on_state_focused(); diff --git a/modules/regex/SCsub b/modules/regex/SCsub index deb9db7591..6fd7cd47f3 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -58,10 +58,10 @@ if env["builtin_pcre2"]: env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)]) env_pcre2.add_source_files(thirdparty_obj, thirdparty_sources) - env.modules_sources += thirdparty_obj pcre2_builtin("16") pcre2_builtin("32") + env.modules_sources += thirdparty_obj # Godot source files diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index c4462ba687..cbeb073ee5 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -357,7 +357,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { audio_frames_wrote = 0; }; -float VideoStreamPlaybackTheora::get_time() const { +double VideoStreamPlaybackTheora::get_time() const { // FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to // systematically return 0. Now that it gives a proper latency, it broke this // code where the delay compensation likely never really worked. @@ -368,7 +368,7 @@ Ref<Texture2D> VideoStreamPlaybackTheora::get_texture() const { return texture; } -void VideoStreamPlaybackTheora::update(float p_delta) { +void VideoStreamPlaybackTheora::update(double p_delta) { if (file.is_null()) { return; } @@ -529,7 +529,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { //printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready); } - float tdiff = videobuf_time - get_time(); + double tdiff = videobuf_time - get_time(); /*If we have lots of extra time, increase the post-processing level.*/ if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) { pp_inc = pp_level < pp_level_max ? 1 : 0; @@ -581,7 +581,7 @@ bool VideoStreamPlaybackTheora::has_loop() const { return false; }; -float VideoStreamPlaybackTheora::get_length() const { +double VideoStreamPlaybackTheora::get_length() const { return 0; }; @@ -593,11 +593,11 @@ int VideoStreamPlaybackTheora::get_loop_count() const { return 0; }; -float VideoStreamPlaybackTheora::get_playback_position() const { +double VideoStreamPlaybackTheora::get_playback_position() const { return get_time(); }; -void VideoStreamPlaybackTheora::seek(float p_time) { +void VideoStreamPlaybackTheora::seek(double p_time) { WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDExtension-provided video streams)."); } diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 00d799dc24..3efb653651 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -64,7 +64,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback { int buffer_data(); int queue_page(ogg_page *page); void video_write(); - float get_time() const; + double get_time() const; bool theora_eos = false; bool vorbis_eos = false; @@ -136,19 +136,19 @@ public: virtual void set_loop(bool p_enable) override; virtual bool has_loop() const override; - virtual float get_length() const override; + virtual double get_length() const override; virtual String get_stream_name() const; virtual int get_loop_count() const; - virtual float get_playback_position() const override; - virtual void seek(float p_time) override; + virtual double get_playback_position() const override; + virtual void seek(double p_time) override; void set_file(const String &p_file); virtual Ref<Texture2D> get_texture() const override; - virtual void update(float p_delta) override; + virtual void update(double p_delta) override; virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override; virtual int get_channels() const override; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 8315eea614..bd220df104 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -223,7 +223,7 @@ bool AudioStreamPlaybackOggVorbis::_alloc_vorbis() { return true; } -void AudioStreamPlaybackOggVorbis::start(float p_from_pos) { +void AudioStreamPlaybackOggVorbis::start(double p_from_pos) { ERR_FAIL_COND(!ready); loop_fade_remaining = FADE_SIZE; active = true; @@ -244,15 +244,15 @@ int AudioStreamPlaybackOggVorbis::get_loop_count() const { return loops; } -float AudioStreamPlaybackOggVorbis::get_playback_position() const { - return float(frames_mixed) / vorbis_data->get_sampling_rate(); +double AudioStreamPlaybackOggVorbis::get_playback_position() const { + return double(frames_mixed) / (double)vorbis_data->get_sampling_rate(); } void AudioStreamPlaybackOggVorbis::tag_used_streams() { vorbis_stream->tag_used(get_playback_position()); } -void AudioStreamPlaybackOggVorbis::seek(float p_time) { +void AudioStreamPlaybackOggVorbis::seek(double p_time) { ERR_FAIL_COND(!ready); ERR_FAIL_COND(vorbis_stream.is_null()); if (!active) { @@ -462,15 +462,15 @@ bool AudioStreamOggVorbis::has_loop() const { return loop; } -void AudioStreamOggVorbis::set_loop_offset(float p_seconds) { +void AudioStreamOggVorbis::set_loop_offset(double p_seconds) { loop_offset = p_seconds; } -float AudioStreamOggVorbis::get_loop_offset() const { +double AudioStreamOggVorbis::get_loop_offset() const { return loop_offset; } -float AudioStreamOggVorbis::get_length() const { +double AudioStreamOggVorbis::get_length() const { ERR_FAIL_COND_V(packet_sequence.is_null(), 0); return packet_sequence->get_length(); } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 0350e1f761..3aa99971be 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -83,14 +83,14 @@ protected: virtual float get_stream_sampling_rate() override; public: - virtual void start(float p_from_pos = 0.0) override; + virtual void start(double p_from_pos = 0.0) override; virtual void stop() override; virtual bool is_playing() const override; virtual int get_loop_count() const override; //times it looped - virtual float get_playback_position() const override; - virtual void seek(float p_time) override; + virtual double get_playback_position() const override; + virtual void seek(double p_time) override; virtual void tag_used_streams() override; @@ -127,8 +127,8 @@ public: void set_loop(bool p_enable); virtual bool has_loop() const override; - void set_loop_offset(float p_seconds); - float get_loop_offset() const; + void set_loop_offset(double p_seconds); + double get_loop_offset() const; void set_bpm(double p_bpm); virtual double get_bpm() const override; @@ -145,7 +145,7 @@ public: void set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence); Ref<OggPacketSequence> get_packet_sequence() const; - virtual float get_length() const override; //if supported, otherwise return 0 + virtual double get_length() const override; //if supported, otherwise return 0 virtual bool is_monophonic() const override; |