diff options
Diffstat (limited to 'modules')
38 files changed, 304 insertions, 58 deletions
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 4938d8bff5..a0578bbd9d 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -149,7 +149,7 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) { int h = p_image->get_height(); bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8); - bool is_hdr = (p_image->get_format() >= Image::FORMAT_RH) && (p_image->get_format() <= Image::FORMAT_RGBE9995); + bool is_hdr = (p_image->get_format() >= Image::FORMAT_RF) && (p_image->get_format() <= Image::FORMAT_RGBE9995); if (!is_ldr && !is_hdr) { return; // Not a usable source format diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index a7d5c406e9..ecd33a5dac 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -11,6 +11,7 @@ def get_doc_classes(): return [ "@GDScript", "GDScript", + "GDScriptSyntaxHighlighter", ] diff --git a/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml b/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml new file mode 100644 index 0000000000..63a9222901 --- /dev/null +++ b/modules/gdscript/doc_classes/GDScriptSyntaxHighlighter.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GDScriptSyntaxHighlighter" inherits="EditorSyntaxHighlighter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + A GDScript syntax highlighter that can be used with [TextEdit] and [CodeEdit] nodes. + </brief_description> + <description> + [b]Note:[/b] This class can only be used for editor plugins because it relies on editor settings. + [codeblocks] + [gdscript] + var code_preview = TextEdit.new() + var highlighter = GDScriptSyntaxHighlighter.new() + code_preview.syntax_highlighter = highlighter + [/gdscript] + [csharp] + var codePreview = new TextEdit(); + var highlighter = new GDScriptSyntaxHighlighter(); + codePreview.SyntaxHighlighter = highlighter; + [/csharp] + [/codeblocks] + </description> + <tutorials> + </tutorials> +</class> diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 54bb152f7f..ecef852b4b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -245,8 +245,26 @@ void GDScriptParser::apply_pending_warnings() { } #endif -void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) { - if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { +void GDScriptParser::override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument) { + if (!for_completion) { + return; + } + if (completion_context.node != p_for_node) { + return; + } + CompletionContext context; + context.type = p_type; + context.current_class = current_class; + context.current_function = current_function; + context.current_suite = current_suite; + context.current_line = tokenizer->get_cursor_line(); + context.current_argument = p_argument; + context.node = p_node; + completion_context = context; +} + +void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument) { + if (!for_completion) { return; } if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { @@ -264,8 +282,8 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node completion_context = context; } -void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) { - if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) { +void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type) { + if (!for_completion) { return; } if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) { @@ -1620,7 +1638,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali advance(); // Arguments. push_completion_call(annotation); - make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true); + make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0); int argument_index = 0; do { if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { @@ -1628,7 +1646,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali break; } - make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true); + make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index); set_last_completion_call_arg(argument_index++); ExpressionNode *argument = parse_expression(false); if (argument == nullptr) { @@ -2569,8 +2587,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_ } LiteralNode *literal = alloc_node<LiteralNode>(); - complete_extents(literal); literal->value = previous.literal; + reset_extents(literal, p_previous_operand); + update_extents(literal); + make_completion_context(COMPLETION_NONE, literal, -1); + complete_extents(literal); return literal; } @@ -3065,12 +3086,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand); Variant::Type builtin_type = get_builtin_type(id->name); if (builtin_type < Variant::VARIANT_MAX) { - make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true); + make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type); is_builtin = true; } } if (!is_builtin) { - make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1, true); + make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1); } } @@ -3195,23 +3216,24 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre push_completion_call(call); int argument_index = 0; do { - make_completion_context(ct, call, argument_index++, true); + make_completion_context(ct, call, argument_index); if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { // Allow for trailing comma. break; } - bool use_identifier_completion = current.cursor_place == GDScriptTokenizerText::CURSOR_END || current.cursor_place == GDScriptTokenizerText::CURSOR_MIDDLE; ExpressionNode *argument = parse_expression(false); if (argument == nullptr) { push_error(R"(Expected expression as the function argument.)"); } else { call->arguments.push_back(argument); - if (argument->type == Node::IDENTIFIER && use_identifier_completion) { - completion_context.type = COMPLETION_IDENTIFIER; + if (argument->type == Node::LITERAL) { + override_completion_context(argument, ct, call, argument_index); } } + ct = COMPLETION_CALL_ARGUMENTS; + argument_index++; } while (match(GDScriptTokenizer::Token::COMMA)); pop_completion_call(); @@ -3224,7 +3246,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { // We want code completion after a DOLLAR even if the current code is invalid. - make_completion_context(COMPLETION_GET_NODE, nullptr, -1, true); + make_completion_context(COMPLETION_GET_NODE, nullptr, -1); if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); @@ -3281,7 +3303,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p path_state = PATH_STATE_SLASH; } - make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++, true); + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); if (match(GDScriptTokenizer::Token::LITERAL)) { if (previous.literal.get_type() != Variant::STRING) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 60ee477656..2999fb11e4 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1455,9 +1455,11 @@ private: } void apply_pending_warnings(); #endif - - void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false); - void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false); + void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1); + void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type); + // In some cases it might become necessary to alter the completion context after parsing a subexpression. + // For example to not override COMPLETE_CALL_ARGUMENTS with COMPLETION_NONE from string literals. + void override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument = -1); void push_completion_call(Node *p_call); void pop_completion_call(); void set_last_completion_call_arg(int p_argument); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 59e387eece..055f8e4110 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -165,6 +165,13 @@ void initialize_gdscript_module(ModuleInitializationLevel p_level) { gdscript_translation_parser_plugin.instantiate(); EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD); + } else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + ClassDB::APIType prev_api = ClassDB::get_current_api(); + ClassDB::set_current_api(ClassDB::API_EDITOR); + + GDREGISTER_CLASS(GDScriptSyntaxHighlighter); + + ClassDB::set_current_api(prev_api); } #endif // TOOLS_ENABLED } diff --git a/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg new file mode 100644 index 0000000000..be9bd510e1 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.cfg @@ -0,0 +1,5 @@ +[output] +include=[ + {"insert_text": "\"property_of_a\""}, + {"insert_text": "\"name\""}, +] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd new file mode 100644 index 0000000000..a8e04a62a7 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/string_literals/argument_options_inside_string_literal.gd @@ -0,0 +1,8 @@ +extends Node + +const A = preload ("res://completion/class_a.notest.gd") + +func _ready() -> void: + var a := A.new() + var tween := get_tree().create_tween() + tween.tween_property(a, "➡") diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.cfg index 871a404e3a..5f08f9c265 100644 --- a/modules/gdscript/tests/scripts/completion/common/identifiers.cfg +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.cfg @@ -11,11 +11,15 @@ include=[ {"display": "func_of_a"}, {"display": "signal_of_a"}, - ; GDScript: self.gd + ; GDScript: identifiers.gd {"display": "test_signal_1"}, {"display": "test_signal_2"}, {"display": "test_var_1"}, {"display": "test_var_2"}, {"display": "test_func_1"}, {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, ] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd new file mode 100644 index 0000000000..91488c25aa --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_call.gd @@ -0,0 +1,18 @@ +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + print(t➡) diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg new file mode 100644 index 0000000000..5f08f9c265 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.cfg @@ -0,0 +1,25 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: identifiers.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.gd index efbafbee8e..a2f5b7bc23 100644 --- a/modules/gdscript/tests/scripts/completion/common/identifiers.gd +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_function_body.gd @@ -12,5 +12,7 @@ func test_func_1(t): func test_func_2(t: int) -> void: pass -func _init(): +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int t➡ diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg new file mode 100644 index 0000000000..5f08f9c265 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.cfg @@ -0,0 +1,25 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: identifiers.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd new file mode 100644 index 0000000000..fed0b869c4 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/identifiers_in_unclosed_call.gd @@ -0,0 +1,22 @@ +# godotengine/godot#92226 +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + print(t➡ + + if true: + pass diff --git a/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg new file mode 100644 index 0000000000..462846c9b2 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.cfg @@ -0,0 +1,26 @@ +scene="res://completion/get_node/get_node.tscn" +[output] +exclude=[ + ; Node + {"display": "add_child"}, + {"display": "owner"}, + {"display": "child_entered_tree"}, + {"display": "add_child"}, + + ; GDScript: class_a.notest.gd + {"display": "property_of_a"}, + {"display": "func_of_a"}, + {"display": "signal_of_a"}, + + ; GDScript: no_completion_in_string.gd + {"display": "test_signal_1"}, + {"display": "test_signal_2"}, + {"display": "test_var_1"}, + {"display": "test_var_2"}, + {"display": "test_func_1"}, + {"display": "test_func_2"}, + {"display": "test_parameter_1"}, + {"display": "test_parameter_2"}, + {"display": "local_test_var_1"}, + {"display": "local_test_var_2"}, +] diff --git a/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd new file mode 100644 index 0000000000..da52af9fe3 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/common/no_completion_in_string.gd @@ -0,0 +1,19 @@ +# godotengine/godot#62945 +extends "res://completion/class_a.notest.gd" + +signal test_signal_1(a) +signal test_signal_2(a: int) + +var test_var_1 +var test_var_2: int + +func test_func_1(t): + pass + +func test_func_2(t: int) -> void: + pass + +func _init(test_parameter_1, test_parameter_2: String): + var local_test_var_1 + var local_test_var_2: int + var a = "➡" diff --git a/modules/gdscript/tests/scripts/completion/common/self.gd b/modules/gdscript/tests/scripts/completion/common/self.gd index 9ad2fbea51..ed181af0c5 100644 --- a/modules/gdscript/tests/scripts/completion/common/self.gd +++ b/modules/gdscript/tests/scripts/completion/common/self.gd @@ -14,3 +14,4 @@ func test_func_2(t: int) -> void: func _init(): self.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/filter/organized_export.gd b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd index 189608904c..9fa9618cee 100644 --- a/modules/gdscript/tests/scripts/completion/filter/organized_export.gd +++ b/modules/gdscript/tests/scripts/completion/filter/organized_export.gd @@ -5,4 +5,5 @@ extends CPUParticles2D @export_subgroup("Test Subgroup") func _init(): - ➡ + t➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg index ae7d34d87d..ae7d34d87d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd index 7710c2d13b..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg index 9c580b711d..9c580b711d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd index 6b29bf5526..6b29bf5526 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg index 446198dd35..446198dd35 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd index 7710c2d13b..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg index ae7d34d87d..ae7d34d87d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd index 97b288334e..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg index 9c580b711d..9c580b711d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd index 402fd1d275..402fd1d275 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg index 446198dd35..446198dd35 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.cfg +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd index 97b288334e..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/local/infered.cfg index 8b68d51a89..8b68d51a89 100644 --- a/modules/gdscript/tests/scripts/completion/types/local/interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/types/local/infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/types/local/interfered.gd b/modules/gdscript/tests/scripts/completion/types/local/infered.gd index f003c366a4..f003c366a4 100644 --- a/modules/gdscript/tests/scripts/completion/types/local/interfered.gd +++ b/modules/gdscript/tests/scripts/completion/types/local/infered.gd diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg b/modules/gdscript/tests/scripts/completion/types/member/infered.cfg index 8b68d51a89..8b68d51a89 100644 --- a/modules/gdscript/tests/scripts/completion/types/member/interfered.cfg +++ b/modules/gdscript/tests/scripts/completion/types/member/infered.cfg diff --git a/modules/gdscript/tests/scripts/completion/types/member/interfered.gd b/modules/gdscript/tests/scripts/completion/types/member/infered.gd index 069abd7891..069abd7891 100644 --- a/modules/gdscript/tests/scripts/completion/types/member/interfered.gd +++ b/modules/gdscript/tests/scripts/completion/types/member/infered.gd diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 5cc2a8026e..901700067d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -241,11 +241,17 @@ namespace Godot.Bridge if (outIconPath != null) { - var iconAttr = scriptType.GetCustomAttributes(inherit: false) + IconAttribute? iconAttr = scriptType.GetCustomAttributes(inherit: false) .OfType<IconAttribute>() .FirstOrDefault(); - *outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path); + if (!string.IsNullOrEmpty(iconAttr?.Path)) + { + string iconPath = iconAttr.Path.IsAbsolutePath() + ? iconAttr.Path.SimplifyPath() + : scriptPathStr.GetBaseDir().PathJoin(iconAttr.Path).SimplifyPath(); + *outIconPath = Marshaling.ConvertStringToNative(iconPath); + } } if (outBaseType != null) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index a5fa89d3bf..f943a3049d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -47,7 +47,7 @@ namespace Godot { get { - real_t detSign = Mathf.Sign(BasisDeterminant()); + real_t detSign = Mathf.Sign(Determinant()); return new Vector2(X.Length(), detSign * Y.Length()); } } @@ -59,7 +59,7 @@ namespace Godot { get { - real_t detSign = Mathf.Sign(BasisDeterminant()); + real_t detSign = Mathf.Sign(Determinant()); return Mathf.Acos(X.Normalized().Dot(detSign * Y.Normalized())) - Mathf.Pi * 0.5f; } } @@ -135,7 +135,7 @@ namespace Godot /// <returns>The inverse transformation matrix.</returns> public readonly Transform2D AffineInverse() { - real_t det = BasisDeterminant(); + real_t det = Determinant(); if (det == 0) throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); @@ -157,15 +157,16 @@ namespace Godot /// <summary> /// Returns the determinant of the basis matrix. If the basis is - /// uniformly scaled, its determinant is the square of the scale. + /// uniformly scaled, then its determinant equals the square of the + /// scale factor. /// - /// A negative determinant means the Y scale is negative. - /// A zero determinant means the basis isn't invertible, - /// and is usually considered invalid. + /// A negative determinant means the basis was flipped, so one part of + /// the scale is negative. A zero determinant means the basis isn't + /// invertible, and is usually considered invalid. /// </summary> /// <returns>The determinant of the basis matrix.</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] - private readonly real_t BasisDeterminant() + public readonly real_t Determinant() { return (X.X * Y.Y) - (X.Y * Y.X); } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index d9a66aa827..6d6231f6fa 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -58,9 +58,14 @@ OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() { HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() { HashMap<String, bool *> request_extensions; + unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source"); + controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source"); + request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext; request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext; - request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext; + if (unobstructed_data_source || controller_data_source) { + request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext; + } return request_extensions; } @@ -141,10 +146,18 @@ void OpenXRHandTrackingExtension::on_process() { void *next_pointer = nullptr; // Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking. - // With this extension we can indicate we accept input from both sources so hand tracking data is consistently provided - // on runtimes that support this. - XrHandTrackingDataSourceEXT data_sources[2] = { XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT }; - XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, 2, data_sources }; + // With this extension we can indicate we wish to accept input from either or both sources. + // This functionality is subject to the abilities of the XR runtime and requires the data source extension. + // Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports. + uint32_t data_source_count = 0; + XrHandTrackingDataSourceEXT data_sources[2]; + if (unobstructed_data_source) { + data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT; + } + if (controller_data_source) { + data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT; + } + XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources }; if (hand_tracking_source_ext) { // If supported include this info next_pointer = &data_source_info; @@ -224,7 +237,9 @@ void OpenXRHandTrackingExtension::on_process() { if (XR_FAILED(result)) { // not successful? then we do nothing. print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); + godot_tracker->invalidate_pose("default"); continue; } @@ -235,8 +250,6 @@ void OpenXRHandTrackingExtension::on_process() { } if (hand_trackers[i].locations.isActive) { - godot_tracker->set_has_tracking_data(true); - // SKELETON_RIG_HUMANOID bone adjustment. This rotation performs: // OpenXR Z+ -> Godot Humanoid Y- (Back along the bone) // OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand) @@ -245,7 +258,6 @@ void OpenXRHandTrackingExtension::on_process() { for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) { const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint]; const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint]; - const XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source; const XrPosef &pose = location.pose; Transform3D transform; @@ -285,23 +297,35 @@ void OpenXRHandTrackingExtension::on_process() { godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius); if (joint == XR_HAND_JOINT_PALM_EXT) { - XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; - if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { - source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED; - } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { - source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER; - } - - godot_tracker->set_hand_tracking_source(source); if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) { + XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source; + + XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; + if (hand_tracking_source_ext) { + if (!data_source.isActive) { + source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED; + } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { + source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED; + } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { + source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER; + } else { + // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them. + WARN_PRINT_ONCE("Unknown active data source found!"); + source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; + } + } + godot_tracker->set_hand_tracking_source(source); + godot_tracker->set_has_tracking_data(true); godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity); } else { + godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); godot_tracker->invalidate_pose("default"); } } } } else { + godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN); godot_tracker->set_has_tracking_data(false); godot_tracker->invalidate_pose("default"); } @@ -349,16 +373,17 @@ XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTra OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const { ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN); - if (hand_tracking_source_ext && hand_trackers[p_hand].data_source.isActive) { - switch (hand_trackers[p_hand].data_source.dataSource) { - case XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT: - return OPENXR_SOURCE_UNOBSTRUCTED; - - case XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT: - return OPENXR_SOURCE_CONTROLLER; - - default: - return OPENXR_SOURCE_UNKNOWN; + if (hand_tracking_source_ext) { + if (!hand_trackers[p_hand].data_source.isActive) { + return OPENXR_SOURCE_NOT_TRACKED; + } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { + return OPENXR_SOURCE_UNOBSTRUCTED; + } else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { + return OPENXR_SOURCE_CONTROLLER; + } else { + // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them. + WARN_PRINT_ONCE("Unknown active data source found!"); + return OPENXR_SOURCE_UNKNOWN; } } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h index f709bc05c2..2c34ff7f21 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.h +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h @@ -48,6 +48,7 @@ public: OPENXR_SOURCE_UNKNOWN, OPENXR_SOURCE_UNOBSTRUCTED, OPENXR_SOURCE_CONTROLLER, + OPENXR_SOURCE_NOT_TRACKED, OPENXR_SOURCE_MAX }; @@ -110,6 +111,8 @@ private: bool hand_tracking_ext = false; bool hand_motion_range_ext = false; bool hand_tracking_source_ext = false; + bool unobstructed_data_source = false; + bool controller_data_source = false; // functions void cleanup_hand_tracking(); |
