diff options
Diffstat (limited to 'modules')
35 files changed, 768 insertions, 123 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 18045e323c..806ab1e3df 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -557,7 +557,7 @@ <description> Export an [int] or [float] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting. If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget. - Hints also allow to indicate the units for the edited value. Using [code]"radians"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock. [code]"degrees"[/code] allows to add a degree sign as a unit suffix. Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string. + Hints also allow to indicate the units for the edited value. Using [code]"radians_as_degrees"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock (the range values are also in degrees). [code]"degrees"[/code] allows to add a degree sign as a unit suffix (the value is unchanged). Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string. See also [constant PROPERTY_HINT_RANGE]. [codeblock] @export_range(0, 20) var number @@ -567,7 +567,7 @@ @export_range(0, 100, 1, "or_greater") var power_percent @export_range(0, 100, 1, "or_greater", "or_less") var health_delta - @export_range(-3.14, 3.14, 0.001, "radians") var angle_radians + @export_range(-180, 180, 0.001, "radians_as_degrees") var angle_radians @export_range(0, 360, 1, "degrees") var angle_degrees @export_range(-8, 8, 2, "suffix:px") var target_offset [/codeblock] diff --git a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd index b8fc8c75dc..28ab080dd2 100644 --- a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd @@ -15,7 +15,7 @@ func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y += gravity * delta - # Handle Jump. + # Handle jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY diff --git a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd index 53bc606c9a..9b0e4be4ed 100644 --- a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd +++ b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd @@ -15,7 +15,7 @@ func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y -= gravity * delta - # Handle Jump. + # Handle jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY diff --git a/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd b/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd index b27b3e5655..547943b910 100644 --- a/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd +++ b/modules/gdscript/editor/script_templates/EditorPlugin/plugin.gd @@ -1,6 +1,7 @@ # meta-description: Basic plugin template + @tool -extends EditorPlugin +extends _BASE_ func _enter_tree() -> void: diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd index 556afe994b..6772ea4a26 100644 --- a/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd +++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd @@ -1,6 +1,7 @@ # meta-description: Basic import script template + @tool -extends EditorScenePostImport +extends _BASE_ # Called by the editor when a scene has this script set as the import script in the import tab. diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd index 875afb4fc0..e8f907f43b 100644 --- a/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd +++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd @@ -1,6 +1,7 @@ # meta-description: Basic import script template (no comments) + @tool -extends EditorScenePostImport +extends _BASE_ func _post_import(scene: Node) -> Object: diff --git a/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd b/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd index fdb8550d43..fee7353f0d 100644 --- a/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd +++ b/modules/gdscript/editor/script_templates/EditorScript/basic_editor_script.gd @@ -1,6 +1,7 @@ # meta-description: Basic editor script template + @tool -extends EditorScript +extends _BASE_ # Called when the script is executed (using File -> Run in Script Editor). diff --git a/modules/gdscript/editor/script_templates/RichTextEffect/default.gd b/modules/gdscript/editor/script_templates/RichTextEffect/default.gd index c79eeb91ec..c7a999ef24 100644 --- a/modules/gdscript/editor/script_templates/RichTextEffect/default.gd +++ b/modules/gdscript/editor/script_templates/RichTextEffect/default.gd @@ -1,15 +1,16 @@ # meta-description: Base template for rich text effects @tool -class_name _CLASS_ +# Having a class name is handy for picking the effect in the Inspector. +class_name RichText_CLASS_ extends _BASE_ # To use this effect: # - Enable BBCode on a RichTextLabel. # - Register this effect on the label. -# - Use [_CLASS_ param=2.0]hello[/_CLASS_] in text. -var bbcode := "_CLASS_" +# - Use [_CLASS_SNAKE_CASE_ param=2.0]hello[/_CLASS_SNAKE_CASE_] in text. +var bbcode := "_CLASS_SNAKE_CASE_" func _process_custom_fx(char_fx: CharFXTransform) -> bool: diff --git a/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd b/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd index 283a95d3b4..458e22dae4 100644 --- a/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd +++ b/modules/gdscript/editor/script_templates/VisualShaderNodeCustom/basic.gd @@ -1,6 +1,7 @@ # meta-description: Visual shader's node plugin template @tool +# Having a class name is required for a custom node. class_name VisualShaderNode_CLASS_ extends _BASE_ @@ -17,7 +18,7 @@ func _get_description() -> String: return "" -func _get_return_icon_type() -> int: +func _get_return_icon_type() -> PortType: return PORT_TYPE_SCALAR @@ -29,7 +30,7 @@ func _get_input_port_name(port: int) -> String: return "" -func _get_input_port_type(port: int) -> int: +func _get_input_port_type(port: int) -> PortType: return PORT_TYPE_SCALAR @@ -41,10 +42,10 @@ func _get_output_port_name(port: int) -> String: return "result" -func _get_output_port_type(port: int) -> int: +func _get_output_port_type(port: int) -> PortType: return PORT_TYPE_SCALAR func _get_code(input_vars: Array[String], output_vars: Array[String], - mode: int, type: int) -> String: + mode: Shader.Mode, type: VisualShader.Type) -> String: return output_vars[0] + " = 0.0;" diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 2fd2ec236a..2b698d75d4 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -195,6 +195,7 @@ public: void clear(GDScript::ClearData *p_clear_data = nullptr); virtual bool is_valid() const override { return valid; } + virtual bool is_abstract() const override { return false; } // GDScript does not support abstract classes. bool inherits_script(const Ref<Script> &p_script) const override; @@ -501,7 +502,9 @@ public: virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override; virtual Script *create_script() const override; - virtual bool has_named_classes() const override; +#ifndef DISABLE_DEPRECATED + virtual bool has_named_classes() const override { return false; } +#endif virtual bool supports_builtin_mode() const override; virtual bool supports_documentation() const override; virtual bool can_inherit_from_file() const override { return true; } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 00d3df8fd0..adfe4a3290 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -76,19 +76,25 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri #endif if (!type_hints) { processed_template = processed_template.replace(": int", "") + .replace(": Shader.Mode", "") + .replace(": VisualShader.Type", "") + .replace(": float", "") .replace(": String", "") .replace(": Array[String]", "") - .replace(": float", "") + .replace(": Node", "") .replace(": CharFXTransform", "") .replace(":=", "=") - .replace(" -> String", "") - .replace(" -> int", "") + .replace(" -> void", "") .replace(" -> bool", "") - .replace(" -> void", ""); + .replace(" -> int", "") + .replace(" -> PortType", "") + .replace(" -> String", "") + .replace(" -> Object", ""); } processed_template = processed_template.replace("_BASE_", p_base_class_name) - .replace("_CLASS_", p_class_name.to_pascal_case()) + .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_identifier()) + .replace("_CLASS_", p_class_name.to_pascal_case().validate_identifier()) .replace("_TS_", _get_indentation()); scr->set_source_code(processed_template); return scr; @@ -191,10 +197,6 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li return true; } -bool GDScriptLanguage::has_named_classes() const { - return false; -} - bool GDScriptLanguage::supports_builtin_mode() const { return true; } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index c0644e089c..be18dee2cf 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -398,7 +398,13 @@ void (*type_init_function_table[])(Variant *) = { #define OPCODES_END #define OPCODES_OUT #define DISPATCH_OPCODE continue +#ifdef _MSC_VER +#define OPCODE_SWITCH(m_test) \ + __assume(m_test <= OPCODE_END); \ + switch (m_test) +#else #define OPCODE_SWITCH(m_test) switch (m_test) +#endif #define OPCODE_BREAK break #define OPCODE_OUT break #endif diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 1e927f9f6e..d6779dc71c 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -346,6 +346,15 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { } } + if (item.kind == lsp::CompletionItemKind::Method) { + bool is_trigger_character = params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter; + bool is_quote_character = params.context.triggerCharacter == "\"" || params.context.triggerCharacter == "'"; + + if (is_trigger_character && is_quote_character && item.insertText.is_quoted()) { + item.insertText = item.insertText.unquote(); + } + } + return item.to_json(true); } diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h index 1ac4267c7b..e09adb74bd 100644 --- a/modules/gdscript/language_server/godot_lsp.h +++ b/modules/gdscript/language_server/godot_lsp.h @@ -1429,6 +1429,17 @@ struct CompletionParams : public TextDocumentPositionParams { TextDocumentPositionParams::load(p_params); context.load(p_params["context"]); } + + Dictionary to_json() { + Dictionary ctx; + ctx["triggerCharacter"] = context.triggerCharacter; + ctx["triggerKind"] = context.triggerKind; + + Dictionary dict; + dict = TextDocumentPositionParams::to_json(); + dict["context"] = ctx; + return dict; + } }; /** diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 6a20768583..b5a53fa1bf 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1291,14 +1291,14 @@ GridMapEditor::GridMapEditor() { search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input)); mode_thumbnail = memnew(Button); - mode_thumbnail->set_flat(true); + mode_thumbnail->set_theme_type_variation("FlatButton"); mode_thumbnail->set_toggle_mode(true); mode_thumbnail->set_pressed(true); hb->add_child(mode_thumbnail); mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_THUMBNAIL)); mode_list = memnew(Button); - mode_list->set_flat(true); + mode_list->set_theme_type_variation("FlatButton"); mode_list->set_toggle_mode(true); mode_list->set_pressed(false); hb->add_child(mode_list); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 26c684f769..95bf848cbf 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -392,10 +392,6 @@ Script *CSharpLanguage::create_script() const { return memnew(CSharpScript); } -bool CSharpLanguage::has_named_classes() const { - return false; -} - bool CSharpLanguage::supports_builtin_mode() const { return false; } @@ -554,13 +550,13 @@ bool CSharpLanguage::handles_global_class_type(const String &p_type) const { String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { Ref<CSharpScript> scr = ResourceLoader::load(p_path, get_type()); - if (!scr.is_valid() || !scr->valid || !scr->global_class) { - // Invalid script or the script is not a global class. - return String(); - } + // Always assign r_base_type and r_icon_path, even if the script + // is not a global one. In the case that it is not a global script, + // return an empty string AFTER assigning the return parameters. + // See GDScriptLanguage::get_global_class_name() in modules/gdscript/gdscript.cpp - String name = scr->class_name; - if (unlikely(name.is_empty())) { + if (!scr.is_valid() || !scr->valid) { + // Invalid script. return String(); } @@ -587,7 +583,8 @@ String CSharpLanguage::get_global_class_name(const String &p_path, String *r_bas *r_base_type = scr->get_instance_base_type(); } } - return name; + + return scr->global_class ? scr->class_name : String(); } String CSharpLanguage::debug_get_error() const { @@ -2300,6 +2297,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) { void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { bool tool = false; bool global_class = false; + bool abstract_class = false; // TODO: Use GDExtension godot_dictionary Array methods_array; @@ -2313,12 +2311,13 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { String icon_path; Ref<CSharpScript> base_script; GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo( - p_script.ptr(), &class_name, &tool, &global_class, &icon_path, + p_script.ptr(), &class_name, &tool, &global_class, &abstract_class, &icon_path, &methods_array, &rpc_functions_dict, &signals_dict, &base_script); p_script->class_name = class_name; p_script->tool = tool; p_script->global_class = global_class; + p_script->abstract_class = abstract_class; p_script->icon_path = icon_path; p_script->rpc_config.clear(); @@ -2408,7 +2407,7 @@ bool CSharpScript::can_instantiate() const { ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive)."); } - return valid && extra_cond; + return valid && !abstract_class && extra_cond; } StringName CSharpScript::get_instance_base_type() const { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index fd9e281e63..2ab80c132d 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -63,6 +63,7 @@ class CSharpScript : public Script { bool tool = false; bool global_class = false; + bool abstract_class = false; bool valid = false; bool reload_invalidated = false; @@ -188,6 +189,9 @@ public: bool is_valid() const override { return valid; } + bool is_abstract() const override { + return abstract_class; + } bool inherits_script(const Ref<Script> &p_script) const override; @@ -425,7 +429,9 @@ public: } String validate_path(const String &p_path) const override; Script *create_script() const override; - bool has_named_classes() const override; +#ifndef DISABLE_DEPRECATED + virtual bool has_named_classes() const override { return false; } +#endif bool supports_builtin_mode() const override; /* TODO? */ int find_function(const String &p_function, const String &p_code) const override { return -1; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 5d6583b404..a78cb0bba9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -25,7 +25,7 @@ namespace Godot.Bridge public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath; public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge; public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass; - public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, godot_bool*, godot_string*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo; + public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, godot_bool*, godot_bool*, godot_string*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo; public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType; public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList; public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 1a7d2d9075..9a7e19024b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -131,6 +132,8 @@ namespace Godot.Bridge // Performance is not critical here as this will be replaced with source generators. Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr); + Debug.Assert(!scriptType.IsAbstract, $"Cannot create script instance. The class '{scriptType.FullName}' is abstract."); + var ctor = scriptType .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(c => c.GetParameters().Length == argCount) @@ -146,7 +149,7 @@ namespace Godot.Bridge else { throw new MissingMemberException( - $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters."); + $"The class '{scriptType.FullName}' does not define a constructor that takes {argCount} parameters."); } } @@ -597,7 +600,7 @@ namespace Godot.Bridge [UnmanagedCallersOnly] internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_string* outClassName, - godot_bool* outTool, godot_bool* outGlobal, godot_string* outIconPath, + godot_bool* outTool, godot_bool* outGlobal, godot_bool* outAbstract, godot_string* outIconPath, godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest, godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript) { @@ -631,9 +634,10 @@ namespace Godot.Bridge var iconAttr = scriptType.GetCustomAttributes(inherit: false) .OfType<IconAttribute>() .FirstOrDefault(); - *outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path); + *outAbstract = scriptType.IsAbstract.ToGodotBool(); + // Methods // Performance is not critical here as this will be replaced with source generators. @@ -799,6 +803,7 @@ namespace Godot.Bridge *outClassName = default; *outTool = godot_bool.False; *outGlobal = godot_bool.False; + *outAbstract = godot_bool.False; *outIconPath = default; *outMethodsDest = NativeFuncs.godotsharp_array_new(); *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new(); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 6ddc688ea0..dac8cdcaef 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -91,7 +91,7 @@ struct ManagedCallbacks { using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *); using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *); using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *); - using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, String *, bool *, bool *, String *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *); + using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, String *, bool *, bool *, bool *, String *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *); using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool); using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add); using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add); diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp index 1c7546436b..89249dd369 100644 --- a/modules/multiplayer/editor/replication_editor.cpp +++ b/modules/multiplayer/editor/replication_editor.cpp @@ -245,24 +245,29 @@ ReplicationEditor::ReplicationEditor() { add_pick_button->connect("pressed", callable_mp(this, &ReplicationEditor::_pick_new_property)); add_pick_button->set_text(TTR("Add property to sync...")); hb->add_child(add_pick_button); + VSeparator *vs = memnew(VSeparator); vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0)); hb->add_child(vs); hb->add_child(memnew(Label(TTR("Path:")))); + np_line_edit = memnew(LineEdit); np_line_edit->set_placeholder(":property"); np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); np_line_edit->connect("text_submitted", callable_mp(this, &ReplicationEditor::_np_text_submitted)); hb->add_child(np_line_edit); + add_from_path_button = memnew(Button); add_from_path_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed)); add_from_path_button->set_text(TTR("Add from path")); hb->add_child(add_from_path_button); + vs = memnew(VSeparator); vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0)); hb->add_child(vs); + pin = memnew(Button); - pin->set_flat(true); + pin->set_theme_type_variation("FlatButton"); pin->set_toggle_mode(true); hb->add_child(pin); diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp index 85948e7547..d6c31ca35e 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp @@ -134,7 +134,7 @@ NavigationMeshEditor::NavigationMeshEditor() { bake_hbox = memnew(HBoxContainer); button_bake = memnew(Button); - button_bake->set_flat(true); + button_bake->set_theme_type_variation("FlatButton"); bake_hbox->add_child(button_bake); button_bake->set_toggle_mode(true); button_bake->set_text(TTR("Bake NavigationMesh")); @@ -142,7 +142,7 @@ NavigationMeshEditor::NavigationMeshEditor() { button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed)); button_reset = memnew(Button); - button_reset->set_flat(true); + button_reset->set_theme_type_variation("FlatButton"); bake_hbox->add_child(button_reset); button_reset->set_text(TTR("Clear NavigationMesh")); button_reset->set_tooltip_text(TTR("Clears the internal NavigationMesh vertices and polygons.")); diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 1bd10f1009..c3a5d82fc4 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -110,6 +110,8 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_htc_controller_extens env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_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_foveation_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_fb_update_swapchain_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp") diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 8d8cbf1a29..d0630626e6 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -77,6 +77,13 @@ Returns [code]true[/code] if the given action set is active. </description> </method> + <method name="is_foveation_supported" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if OpenXRs foveation extension is supported, the interface must be initialised before this returns a valid value. + [b]Note:[/b] This feature is only available on the compatibility renderer and currently only available on some stand alone headsets. For Vulkan set [member Viewport.vrs_mode] to [code]VRS_XR[/code] on desktop. + </description> + </method> <method name="set_action_set_active"> <return type="void" /> <param index="0" name="name" type="String" /> @@ -98,6 +105,12 @@ <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> The display refresh rate for the current HMD. Only functional if this feature is supported by the OpenXR runtime and after the interface has been initialized. </member> + <member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false"> + Enable dynamic foveation adjustment, the interface must be initialised before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level]. + </member> + <member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0"> + Set foveation level from 0 (off) to 3 (high), the interface must be initialised before this is accessible. + </member> <member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0"> The render size multiplier for the current HMD. Must be set before the interface has been initialized. </member> diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp new file mode 100644 index 0000000000..7277f85b12 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* openxr_fb_foveation_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_foveation_extension.h" +#include "core/config/project_settings.h" + +OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::singleton = nullptr; + +OpenXRFBFoveationExtension *OpenXRFBFoveationExtension::get_singleton() { + return singleton; +} + +OpenXRFBFoveationExtension::OpenXRFBFoveationExtension(const String &p_rendering_driver) { + singleton = this; + rendering_driver = p_rendering_driver; + swapchain_update_state_ext = OpenXRFBUpdateSwapchainExtension::get_singleton(); + int fov_level = GLOBAL_GET("xr/openxr/foveation_level"); + if (fov_level >= 0 && fov_level < 4) { + foveation_level = XrFoveationLevelFB(fov_level); + } + bool fov_dyn = GLOBAL_GET("xr/openxr/foveation_dynamic"); + foveation_dynamic = fov_dyn ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB; + + swapchain_create_info_foveation_fb.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB; + swapchain_create_info_foveation_fb.next = nullptr; + swapchain_create_info_foveation_fb.flags = 0; +} + +OpenXRFBFoveationExtension::~OpenXRFBFoveationExtension() { + singleton = nullptr; + swapchain_update_state_ext = nullptr; +} + +HashMap<String, bool *> OpenXRFBFoveationExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + if (rendering_driver == "vulkan") { + // This is currently only supported on OpenGL, but we may add Vulkan support in the future... + + } else if (rendering_driver == "opengl3") { + request_extensions[XR_FB_FOVEATION_EXTENSION_NAME] = &fb_foveation_ext; + request_extensions[XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME] = &fb_foveation_configuration_ext; + } + + return request_extensions; +} + +void OpenXRFBFoveationExtension::on_instance_created(const XrInstance p_instance) { + if (fb_foveation_ext) { + EXT_INIT_XR_FUNC(xrCreateFoveationProfileFB); + EXT_INIT_XR_FUNC(xrDestroyFoveationProfileFB); + } + + if (fb_foveation_configuration_ext) { + // nothing to register here... + } +} + +void OpenXRFBFoveationExtension::on_instance_destroyed() { + fb_foveation_ext = false; + fb_foveation_configuration_ext = false; +} + +bool OpenXRFBFoveationExtension::is_enabled() const { + return swapchain_update_state_ext != nullptr && swapchain_update_state_ext->is_enabled() && fb_foveation_ext && fb_foveation_configuration_ext; +} + +void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { + if (is_enabled()) { + swapchain_create_info_foveation_fb.next = p_next_pointer; + return &swapchain_create_info_foveation_fb; + } else { + return p_next_pointer; + } +} + +void OpenXRFBFoveationExtension::on_state_ready() { + update_profile(); +} + +XrFoveationLevelFB OpenXRFBFoveationExtension::get_foveation_level() const { + return foveation_level; +} + +void OpenXRFBFoveationExtension::set_foveation_level(XrFoveationLevelFB p_foveation_level) { + foveation_level = p_foveation_level; + + // Update profile will do nothing if we're not yet initialised + update_profile(); +} + +XrFoveationDynamicFB OpenXRFBFoveationExtension::get_foveation_dynamic() const { + return foveation_dynamic; +} + +void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic) { + foveation_dynamic = p_foveation_dynamic; + + // Update profile will do nothing if we're not yet initialised + update_profile(); +} + +void OpenXRFBFoveationExtension::update_profile() { + if (!is_enabled()) { + return; + } + + XrFoveationLevelProfileCreateInfoFB level_profile_create_info; + level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB; + level_profile_create_info.next = nullptr; + level_profile_create_info.level = foveation_level; + level_profile_create_info.verticalOffset = 0.0f; + level_profile_create_info.dynamic = foveation_dynamic; + + XrFoveationProfileCreateInfoFB profile_create_info; + profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB; + profile_create_info.next = &level_profile_create_info; + + XrFoveationProfileFB foveation_profile; + XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + return; + } + + XrSwapchainStateFoveationFB foveation_update_state; + foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB; + foveation_update_state.profile = foveation_profile; + + result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + + // We still want to destroy our profile so keep going... + } + + result = xrDestroyFoveationProfileFB(foveation_profile); + if (XR_FAILED(result)) { + print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + } +} diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h new file mode 100644 index 0000000000..1c5e722731 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* openxr_fb_foveation_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_FOVEATION_EXTENSION_H +#define OPENXR_FB_FOVEATION_EXTENSION_H + +// This extension implements the FB Foveation extension. +// This is an extension Meta added due to VRS being unavailable on Android. +// Other Android based devices are implementing this as well, see: +// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_foveation + +// Note: Currently we only support this for OpenGL. +// This extension works on enabling foveated rendering on the swapchain. +// Vulkan does not render 3D content directly to the swapchain image +// hence this extension can't be used. + +#include "../openxr_api.h" +#include "../util.h" +#include "openxr_extension_wrapper.h" +#include "openxr_fb_update_swapchain_extension.h" + +class OpenXRFBFoveationExtension : public OpenXRExtensionWrapper { +public: + static OpenXRFBFoveationExtension *get_singleton(); + + OpenXRFBFoveationExtension(const String &p_rendering_driver); + virtual ~OpenXRFBFoveationExtension() override; + + virtual HashMap<String, bool *> get_requested_extensions() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; + + virtual void on_state_ready() override; + + bool is_enabled() const; + + XrFoveationLevelFB get_foveation_level() const; + void set_foveation_level(XrFoveationLevelFB p_foveation_level); + + XrFoveationDynamicFB get_foveation_dynamic() const; + void set_foveation_dynamic(XrFoveationDynamicFB p_foveation_dynamic); + +private: + static OpenXRFBFoveationExtension *singleton; + + // Setup + String rendering_driver; + bool fb_foveation_ext = false; + bool fb_foveation_configuration_ext = false; + + // Configuration + XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB; + XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB; + + void update_profile(); + + // Enable foveation on this swapchain + XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb; + OpenXRFBUpdateSwapchainExtension *swapchain_update_state_ext = nullptr; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC3(xrCreateFoveationProfileFB, (XrSession), session, (const XrFoveationProfileCreateInfoFB *), create_info, (XrFoveationProfileFB *), profile); + EXT_PROTO_XRRESULT_FUNC1(xrDestroyFoveationProfileFB, (XrFoveationProfileFB), profile); +}; + +#endif // OPENXR_FB_FOVEATION_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp new file mode 100644 index 0000000000..1289183ea4 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* openxr_fb_update_swapchain_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_update_swapchain_extension.h" + +// always include this as late as possible +#include "../openxr_platform_inc.h" + +OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr; + +OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::get_singleton() { + return singleton; +} + +OpenXRFBUpdateSwapchainExtension::OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver) { + singleton = this; + rendering_driver = p_rendering_driver; +} + +OpenXRFBUpdateSwapchainExtension::~OpenXRFBUpdateSwapchainExtension() { + singleton = nullptr; +} + +HashMap<String, bool *> OpenXRFBUpdateSwapchainExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME] = &fb_swapchain_update_state_ext; + + if (rendering_driver == "vulkan") { +#ifdef XR_USE_GRAPHICS_API_VULKAN + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME] = &fb_swapchain_update_state_vulkan_ext; +#endif + } else if (rendering_driver == "opengl3") { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + request_extensions[XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME] = &fb_swapchain_update_state_opengles_ext; +#endif + } + + return request_extensions; +} + +void OpenXRFBUpdateSwapchainExtension::on_instance_created(const XrInstance p_instance) { + if (fb_swapchain_update_state_ext) { + EXT_INIT_XR_FUNC(xrUpdateSwapchainFB); + EXT_INIT_XR_FUNC(xrGetSwapchainStateFB); + } + + if (fb_swapchain_update_state_vulkan_ext) { + // nothing to register here... + } + + if (fb_swapchain_update_state_opengles_ext) { + // nothing to register here... + } +} + +void OpenXRFBUpdateSwapchainExtension::on_instance_destroyed() { + fb_swapchain_update_state_ext = false; + fb_swapchain_update_state_vulkan_ext = false; + fb_swapchain_update_state_opengles_ext = false; +} + +bool OpenXRFBUpdateSwapchainExtension::is_enabled() const { + if (rendering_driver == "vulkan") { + return fb_swapchain_update_state_ext && fb_swapchain_update_state_vulkan_ext; + } else if (rendering_driver == "opengl3") { +#ifdef XR_USE_GRAPHICS_API_OPENGL_ES + return fb_swapchain_update_state_ext && fb_swapchain_update_state_opengles_ext; +#else + return fb_swapchain_update_state_ext; +#endif + } + + return false; +} diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h new file mode 100644 index 0000000000..a02b550e58 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* openxr_fb_update_swapchain_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_UPDATE_SWAPCHAIN_EXTENSION_H +#define OPENXR_FB_UPDATE_SWAPCHAIN_EXTENSION_H + +// This extension implements the FB update swapchain extension. +// This is an extension Meta added to further configure the swapchain. +// Other Android based devices are implementing this as well, see: +// https://github.khronos.org/OpenXR-Inventory/extension_support.html#XR_FB_swapchain_update_state + +#include "../openxr_api.h" +#include "../util.h" +#include "openxr_extension_wrapper.h" + +class OpenXRFBUpdateSwapchainExtension : public OpenXRExtensionWrapper { + friend class OpenXRFBFoveationExtension; + +public: + static OpenXRFBUpdateSwapchainExtension *get_singleton(); + + OpenXRFBUpdateSwapchainExtension(const String &p_rendering_driver); + virtual ~OpenXRFBUpdateSwapchainExtension() override; + + virtual HashMap<String, bool *> get_requested_extensions() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + bool is_enabled() const; + +private: + static OpenXRFBUpdateSwapchainExtension *singleton; + + // Setup + String rendering_driver; + bool fb_swapchain_update_state_ext = false; + bool fb_swapchain_update_state_vulkan_ext = false; + bool fb_swapchain_update_state_opengles_ext = false; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC2(xrUpdateSwapchainFB, (XrSwapchain), swapchain, (const XrSwapchainStateBaseHeaderFB *), state); + EXT_PROTO_XRRESULT_FUNC2(xrGetSwapchainStateFB, (XrSwapchain), swapchain, (XrSwapchainStateBaseHeaderFB *), state); +}; + +#endif // OPENXR_FB_UPDATE_SWAPCHAIN_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_opengl_extension.h b/modules/openxr/extensions/openxr_opengl_extension.h index 1537f13067..3b0aa0bce9 100644 --- a/modules/openxr/extensions/openxr_opengl_extension.h +++ b/modules/openxr/extensions/openxr_opengl_extension.h @@ -39,38 +39,8 @@ #include "core/templates/vector.h" -#ifdef ANDROID_ENABLED -#define XR_USE_GRAPHICS_API_OPENGL_ES -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#else -#define XR_USE_GRAPHICS_API_OPENGL -#endif - -#ifdef WINDOWS_ENABLED -// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform -// however due to the way the openxr headers are put together, we have no choice. -#include <windows.h> -#endif - -#ifdef X11_ENABLED -#define GL_GLEXT_PROTOTYPES 1 -#define GL3_PROTOTYPES 1 -#include "thirdparty/glad/glad/gl.h" -#include "thirdparty/glad/glad/glx.h" - -#include <X11/Xlib.h> -#endif - -#ifdef ANDROID_ENABLED -// The jobject type from jni.h is used by openxr_platform.h on Android. -#include <jni.h> -#endif - -// Include platform dependent structs. -#include <openxr/openxr_platform.h> +// always include this as late as possible +#include "../openxr_platform_inc.h" class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper { public: diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h index 4add6f6fa2..f31621fda0 100644 --- a/modules/openxr/extensions/openxr_vulkan_extension.h +++ b/modules/openxr/extensions/openxr_vulkan_extension.h @@ -36,24 +36,9 @@ #include "openxr_extension_wrapper.h" #include "core/templates/vector.h" -#include "drivers/vulkan/vulkan_context.h" -// Need to include Vulkan so we know of type definitions. -#define XR_USE_GRAPHICS_API_VULKAN - -#ifdef WINDOWS_ENABLED -// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform -// however due to the way the openxr headers are put together, we have no choice. -#include <windows.h> -#endif - -#ifdef ANDROID_ENABLED -// The jobject type from jni.h is used by openxr_platform.h on Android. -#include <jni.h> -#endif - -// Include platform dependent structs. -#include <openxr/openxr_platform.h> +// always include this as late as possible +#include "../openxr_platform_inc.h" class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks { public: diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 972b6539e3..b1c7ab1615 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -43,30 +43,7 @@ #include "editor/editor_settings.h" #endif -// We need to have all the graphics API defines before the Vulkan or OpenGL -// extensions are included, otherwise we'll only get one graphics API. -#ifdef VULKAN_ENABLED -#define XR_USE_GRAPHICS_API_VULKAN -#endif -#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) -#ifdef ANDROID_ENABLED -#define XR_USE_GRAPHICS_API_OPENGL_ES -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#else -#define XR_USE_GRAPHICS_API_OPENGL -#endif // ANDROID_ENABLED -#ifdef X11_ENABLED -#define GL_GLEXT_PROTOTYPES 1 -#define GL3_PROTOTYPES 1 -#include "thirdparty/glad/glad/gl.h" -#include "thirdparty/glad/glad/glx.h" - -#include <X11/Xlib.h> -#endif // X11_ENABLED -#endif // GLES_ENABLED +#include "openxr_platform_inc.h" #ifdef VULKAN_ENABLED #include "extensions/openxr_vulkan_extension.h" @@ -78,7 +55,9 @@ #include "extensions/openxr_composition_layer_depth_extension.h" #include "extensions/openxr_fb_display_refresh_rate_extension.h" +#include "extensions/openxr_fb_foveation_extension.h" #include "extensions/openxr_fb_passthrough_extension_wrapper.h" +#include "extensions/openxr_fb_update_swapchain_extension.h" #ifdef ANDROID_ENABLED #define OPENXR_LOADER_NAME "libopenxr_loader.so" @@ -1340,6 +1319,10 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) { ERR_FAIL_V_MSG(false, "OpenXR: Unsupported rendering device."); } + // Also register our rendering extensions + register_extension_wrapper(memnew(OpenXRFBUpdateSwapchainExtension(p_rendering_driver))); + register_extension_wrapper(memnew(OpenXRFBFoveationExtension(p_rendering_driver))); + // initialize for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_before_instance_created(); @@ -1859,6 +1842,10 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) { return true; } +XrSwapchain OpenXRAPI::get_color_swapchain() { + return swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain; +} + RID OpenXRAPI::get_color_texture() { if (swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) { return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_COLOR].image_index); @@ -2010,6 +1997,55 @@ void OpenXRAPI::set_render_target_size_multiplier(double multiplier) { render_target_size_multiplier = multiplier; } +bool OpenXRAPI::is_foveation_supported() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + return fov_ext != nullptr && fov_ext->is_enabled(); +} + +int OpenXRAPI::get_foveation_level() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + switch (fov_ext->get_foveation_level()) { + case XR_FOVEATION_LEVEL_NONE_FB: + return 0; + case XR_FOVEATION_LEVEL_LOW_FB: + return 1; + case XR_FOVEATION_LEVEL_MEDIUM_FB: + return 2; + case XR_FOVEATION_LEVEL_HIGH_FB: + return 3; + default: + return 0; + } + } + + return 0; +} + +void OpenXRAPI::set_foveation_level(int p_foveation_level) { + ERR_FAIL_UNSIGNED_INDEX(p_foveation_level, 4); + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + XrFoveationLevelFB levels[] = { XR_FOVEATION_LEVEL_NONE_FB, XR_FOVEATION_LEVEL_LOW_FB, XR_FOVEATION_LEVEL_MEDIUM_FB, XR_FOVEATION_LEVEL_HIGH_FB }; + fov_ext->set_foveation_level(levels[p_foveation_level]); + } +} + +bool OpenXRAPI::get_foveation_dynamic() const { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + return fov_ext->get_foveation_dynamic() == XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB; + } + return false; +} + +void OpenXRAPI::set_foveation_dynamic(bool p_foveation_dynamic) { + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + if (fov_ext != nullptr && fov_ext->is_enabled()) { + fov_ext->set_foveation_dynamic(p_foveation_dynamic ? XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_FB : XR_FOVEATION_DYNAMIC_DISABLED_FB); + } +} + OpenXRAPI::OpenXRAPI() { // OpenXRAPI is only constructed if OpenXR is enabled. singleton = this; diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 26de535153..89f8f3cbec 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -54,7 +54,6 @@ // Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set. // forward declarations, we don't want to include these fully -class OpenXRVulkanExtension; class OpenXRInterface; class OpenXRAPI { @@ -356,6 +355,7 @@ public: void pre_render(); bool pre_draw_viewport(RID p_render_target); + XrSwapchain get_color_swapchain(); RID get_color_texture(); RID get_depth_texture(); void post_draw_viewport(RID p_render_target); @@ -370,6 +370,15 @@ public: double get_render_target_size_multiplier() const; void set_render_target_size_multiplier(double multiplier); + // Foveation settings + bool is_foveation_supported() const; + + int get_foveation_level() const; + void set_foveation_level(int p_foveation_level); + + bool get_foveation_dynamic() const; + void set_foveation_dynamic(bool p_foveation_dynamic); + // action map String get_default_action_map_resource_name(); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index cf8d1654b1..7b1530677f 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -54,10 +54,23 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier"); + // Foveation level + ClassDB::bind_method(D_METHOD("is_foveation_supported"), &OpenXRInterface::is_foveation_supported); + + ClassDB::bind_method(D_METHOD("get_foveation_level"), &OpenXRInterface::get_foveation_level); + ClassDB::bind_method(D_METHOD("set_foveation_level", "foveation_level"), &OpenXRInterface::set_foveation_level); + ADD_PROPERTY(PropertyInfo(Variant::INT, "foveation_level"), "set_foveation_level", "get_foveation_level"); + + ClassDB::bind_method(D_METHOD("get_foveation_dynamic"), &OpenXRInterface::get_foveation_dynamic); + ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic"); + + // Action sets ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets); + // Refresh rates ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); // Hand tracking. @@ -740,6 +753,46 @@ void OpenXRInterface::set_render_target_size_multiplier(double multiplier) { } } +bool OpenXRInterface::is_foveation_supported() const { + if (openxr_api == nullptr) { + return false; + } else { + return openxr_api->is_foveation_supported(); + } +} + +int OpenXRInterface::get_foveation_level() const { + if (openxr_api == nullptr) { + return 0; + } else { + return openxr_api->get_foveation_level(); + } +} + +void OpenXRInterface::set_foveation_level(int p_foveation_level) { + if (openxr_api == nullptr) { + return; + } else { + openxr_api->set_foveation_level(p_foveation_level); + } +} + +bool OpenXRInterface::get_foveation_dynamic() const { + if (openxr_api == nullptr) { + return false; + } else { + return openxr_api->get_foveation_dynamic(); + } +} + +void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) { + if (openxr_api == nullptr) { + return; + } else { + openxr_api->set_foveation_dynamic(p_foveation_dynamic); + } +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 81efbd6777..38cf4bdbe8 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -130,6 +130,14 @@ public: double get_render_target_size_multiplier() const; void set_render_target_size_multiplier(double multiplier); + bool is_foveation_supported() const; + + int get_foveation_level() const; + void set_foveation_level(int p_foveation_level); + + bool get_foveation_dynamic() const; + void set_foveation_dynamic(bool p_foveation_dynamic); + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/openxr/openxr_platform_inc.h b/modules/openxr/openxr_platform_inc.h new file mode 100644 index 0000000000..6288d1e380 --- /dev/null +++ b/modules/openxr/openxr_platform_inc.h @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* openxr_platform_inc.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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_PLATFORM_INC_H +#define OPENXR_PLATFORM_INC_H + +// In various places we need to include platform definitions but we can't +// include these in our normal header files as we'll end up with issues. + +#ifdef VULKAN_ENABLED +#define XR_USE_GRAPHICS_API_VULKAN +#include "drivers/vulkan/vulkan_context.h" +#endif // VULKAN_ENABLED + +#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) +#ifdef ANDROID_ENABLED +#define XR_USE_GRAPHICS_API_OPENGL_ES +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#else +#define XR_USE_GRAPHICS_API_OPENGL +#endif // ANDROID_ENABLED +#ifdef X11_ENABLED +#define GL_GLEXT_PROTOTYPES 1 +#define GL3_PROTOTYPES 1 +#include "thirdparty/glad/glad/gl.h" +#include "thirdparty/glad/glad/glx.h" +#endif // X11_ENABLED +#endif // defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) + +#ifdef X11_ENABLED +#include <X11/Xlib.h> +#endif // X11_ENABLED + +#ifdef WINDOWS_ENABLED +// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform +// however due to the way the openxr headers are put together, we have no choice. +#include <windows.h> +#endif // WINDOWS_ENABLED + +#ifdef ANDROID_ENABLED +// The jobject type from jni.h is used by openxr_platform.h on Android. +#include <jni.h> +#endif // ANDROID_ENABLED + +// Include platform dependent structs. +#include <openxr/openxr_platform.h> + +#endif // OPENXR_PLATFORM_INC_H |
