summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/fbx/fbx_document.cpp7
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp14
-rw-r--r--modules/gdscript/gdscript_compiler.cpp6
-rw-r--r--modules/gdscript/gdscript_compiler.h6
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp3
-rw-r--r--modules/gdscript/gdscript_tokenizer_buffer.cpp6
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp23
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/constants.out32
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/static_typing.out20
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_constant.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_constant.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.gd1
-rw-r--r--modules/gltf/gltf_document.cpp6
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp2
-rw-r--r--modules/mono/csharp_script.cpp51
-rw-r--r--modules/mono/editor/bindings_generator.cpp26
-rw-r--r--modules/mono/editor/bindings_generator.h16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs41
-rw-r--r--modules/navigation/2d/godot_navigation_server_2d.cpp28
-rw-r--r--modules/navigation/2d/godot_navigation_server_2d.h3
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp64
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.h15
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.cpp22
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.h3
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp62
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.h15
-rw-r--r--modules/navigation/3d/navigation_mesh_generator.cpp2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp1
-rw-r--r--modules/openxr/doc_classes/OpenXRCompositionLayer.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml7
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml14
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp33
-rw-r--r--modules/openxr/openxr_interface.cpp69
-rw-r--r--modules/openxr/openxr_interface.h4
-rw-r--r--modules/openxr/scene/openxr_hand.cpp37
-rw-r--r--modules/openxr/scene/openxr_hand.h14
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml4
-rw-r--r--modules/webxr/webxr_interface.compat.inc41
-rw-r--r--modules/webxr/webxr_interface.cpp1
-rw-r--r--modules/webxr/webxr_interface.h9
-rw-r--r--modules/webxr/webxr_interface_js.cpp14
-rw-r--r--modules/webxr/webxr_interface_js.h6
51 files changed, 567 insertions, 214 deletions
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index bde23289f4..44a929d285 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -1629,6 +1629,9 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF
active_skeleton = skeleton;
current_node = active_skeleton;
+ if (active_skeleton) {
+ p_scene_parent = active_skeleton;
+ }
if (requires_extra_node) {
current_node = nullptr;
@@ -2019,8 +2022,8 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool
GLTFNodeIndex fbx_root = state->root_nodes.write[0];
Node *fbx_root_node = state->get_scene_node(fbx_root);
Node *root = fbx_root_node;
- if (fbx_root_node && fbx_root_node->get_parent()) {
- root = fbx_root_node->get_parent();
+ if (root && root->get_owner() && root->get_owner() != root) {
+ root = root->get_owner();
}
ERR_FAIL_NULL_V(root, nullptr);
_process_mesh_instances(state, root);
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index b198338ff0..ec75663e97 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1957,6 +1957,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
parser->push_warning(p_assignable, GDScriptWarning::UNTYPED_DECLARATION, declaration_type, p_assignable->identifier->name);
}
+ } else if (specified_type.kind == GDScriptParser::DataType::ENUM && p_assignable->initializer == nullptr) {
+ // Warn about enum variables without default value. Unless the enum defines the "0" value, then it's fine.
+ bool has_zero_value = false;
+ for (const KeyValue<StringName, int64_t> &kv : specified_type.enum_values) {
+ if (kv.value == 0) {
+ has_zero_value = true;
+ break;
+ }
+ }
+ if (!has_zero_value) {
+ parser->push_warning(p_assignable, GDScriptWarning::ENUM_VARIABLE_WITHOUT_DEFAULT, p_assignable->identifier->name);
+ }
}
#endif
@@ -1985,7 +1997,7 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
#ifdef DEBUG_ENABLED
if (p_is_local) {
- if (p_constant->usages == 0) {
+ if (p_constant->usages == 0 && !String(p_constant->identifier->name).begins_with("_")) {
parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
}
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index d706c1b101..c526d9c0a4 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -250,7 +250,7 @@ static bool _can_use_validate_call(const MethodBind *p_method, const Vector<GDSc
return true;
}
-GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
+GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer) {
if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) {
return codegen.add_constant(p_expression->reduced_value);
}
@@ -781,9 +781,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool named = subscript->is_attribute;
StringName name;
GDScriptCodeGenerator::Address index;
- if (p_index_addr.mode != GDScriptCodeGenerator::Address::NIL) {
- index = p_index_addr;
- } else if (subscript->is_attribute) {
+ if (subscript->is_attribute) {
if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
GDScriptParser::IdentifierNode *identifier = subscript->attribute;
HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name);
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 0adbe1ed8e..637d61ca3b 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -149,13 +149,9 @@ class GDScriptCompiler {
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
- Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
-
GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype = true);
- GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false);
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses);
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 2940af585d..5b1639e250 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1455,10 +1455,11 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
if (_peek() != '\n') {
return make_error("Expected new line after \"\\\".");
}
- continuation_lines.push_back(line);
_advance();
newline(false);
line_continuation = true;
+ _skip_whitespace(); // Skip whitespace/comment lines after `\`. See GH-89403.
+ continuation_lines.push_back(line);
return scan(); // Recurse to get next token.
}
diff --git a/modules/gdscript/gdscript_tokenizer_buffer.cpp b/modules/gdscript/gdscript_tokenizer_buffer.cpp
index db523ea941..e53bc5bc41 100644
--- a/modules/gdscript/gdscript_tokenizer_buffer.cpp
+++ b/modules/gdscript/gdscript_tokenizer_buffer.cpp
@@ -285,9 +285,9 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code,
// Remove continuation lines from map.
for (int line : tokenizer.get_continuation_lines()) {
- if (rev_token_lines.has(line + 1)) {
- token_lines.erase(rev_token_lines[line + 1]);
- token_columns.erase(rev_token_lines[line + 1]);
+ if (rev_token_lines.has(line)) {
+ token_lines.erase(rev_token_lines[line]);
+ token_columns.erase(rev_token_lines[line]);
}
}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 708966a0a8..48a0abe617 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -126,6 +126,9 @@ String GDScriptWarning::get_message() const {
case INT_AS_ENUM_WITHOUT_MATCH:
CHECK_SYMBOLS(3);
return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]);
+ case ENUM_VARIABLE_WITHOUT_DEFAULT:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The variable "%s" has an enum type and does not set an explicit default value. The default will be set to "0".)", symbols[0]);
case EMPTY_FILE:
return "Empty script file.";
case DEPRECATED_KEYWORD:
@@ -221,6 +224,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"NARROWING_CONVERSION",
"INT_AS_ENUM_WITHOUT_CAST",
"INT_AS_ENUM_WITHOUT_MATCH",
+ "ENUM_VARIABLE_WITHOUT_DEFAULT",
"EMPTY_FILE",
"DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 93c232a0f8..3ad9488138 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -78,6 +78,7 @@ public:
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting.
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
+ ENUM_VARIABLE_WITHOUT_DEFAULT, // A variable with an enum type does not have a default value. The default will be set to `0` instead of the first enum value.
EMPTY_FILE, // A script file is empty.
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
@@ -129,6 +130,7 @@ public:
WARN, // NARROWING_CONVERSION
WARN, // INT_AS_ENUM_WITHOUT_CAST
WARN, // INT_AS_ENUM_WITHOUT_MATCH
+ WARN, // ENUM_VARIABLE_WITHOUT_DEFAULT
WARN, // EMPTY_FILE
WARN, // DEPRECATED_KEYWORD
WARN, // RENAMED_IN_GODOT_4_HINT
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index a0329eb8d2..e3d16eaf42 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -300,14 +300,23 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
#endif
String out_file = next.get_basename() + ".out";
- if (!is_generating && !dir->file_exists(out_file)) {
- ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
- }
- GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
- if (binary_tokens) {
- test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ ERR_FAIL_COND_V_MSG(!is_generating && !dir->file_exists(out_file), false, "Could not find output file for " + next);
+
+ if (next.ends_with(".bin.gd")) {
+ // Test text mode first.
+ GDScriptTest text_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ tests.push_back(text_test);
+ // Test binary mode even without `--use-binary-tokens`.
+ GDScriptTest bin_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ bin_test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ tests.push_back(bin_test);
+ } else {
+ GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ if (binary_tokens) {
+ test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ }
+ tests.push_back(test);
}
- tests.push_back(test);
}
}
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
new file mode 100644
index 0000000000..13e3edf93f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
@@ -0,0 +1,9 @@
+enum HasZero { A = 0, B = 1 }
+enum HasNoZero { A = 1, B = 2 }
+var has_zero: HasZero # No warning, because the default `0` is valid.
+var has_no_zero: HasNoZero # Warning, because there is no `0` in the enum.
+
+
+func test():
+ print(has_zero)
+ print(has_no_zero)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
new file mode 100644
index 0000000000..ae40e0bc8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> ENUM_VARIABLE_WITHOUT_DEFAULT
+>> The variable "has_no_zero" has an enum type and does not set an explicit default value. The default will be set to "0".
+0
+0
diff --git a/modules/gdscript/tests/scripts/parser/features/constants.out b/modules/gdscript/tests/scripts/parser/features/constants.out
index 7ec33470d3..d73c5eb7cd 100644
--- a/modules/gdscript/tests/scripts/parser/features/constants.out
+++ b/modules/gdscript/tests/scripts/parser/features/constants.out
@@ -1,33 +1 @@
GDTEST_OK
->> WARNING
->> Line: 2
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_TEST" is declared but never used in the block. If this is intended, prefix it with an underscore: "__TEST".
->> WARNING
->> Line: 3
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_STRING" is declared but never used in the block. If this is intended, prefix it with an underscore: "__STRING".
->> WARNING
->> Line: 4
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_VECTOR" is declared but never used in the block. If this is intended, prefix it with an underscore: "__VECTOR".
->> WARNING
->> Line: 5
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_ARRAY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__ARRAY".
->> WARNING
->> Line: 6
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_DICTIONARY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__DICTIONARY".
->> WARNING
->> Line: 9
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_HELLO" is declared but never used in the block. If this is intended, prefix it with an underscore: "__HELLO".
->> WARNING
->> Line: 10
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INFINITY" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INFINITY".
->> WARNING
->> Line: 11
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_NOT_A_NUMBER" is declared but never used in the block. If this is intended, prefix it with an underscore: "__NOT_A_NUMBER".
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
new file mode 100644
index 0000000000..cb0bc94d2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
@@ -0,0 +1,12 @@
+# GH-89403
+
+func test():
+ var x := 1
+ if x == 0 \
+ # Comment.
+ # Comment.
+ and (x < 1 or x > 2) \
+ # Comment.
+ and x != 3:
+ pass
+ print("Ok")
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
new file mode 100644
index 0000000000..0e9f482af4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Ok
diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.out b/modules/gdscript/tests/scripts/parser/features/static_typing.out
index 40a8f97416..d73c5eb7cd 100644
--- a/modules/gdscript/tests/scripts/parser/features/static_typing.out
+++ b/modules/gdscript/tests/scripts/parser/features/static_typing.out
@@ -1,21 +1 @@
GDTEST_OK
->> WARNING
->> Line: 11
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INTEGER" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER".
->> WARNING
->> Line: 12
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INTEGER_REDUNDANT_TYPED" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_TYPED".
->> WARNING
->> Line: 13
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INTEGER_REDUNDANT_TYPED2" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_TYPED2".
->> WARNING
->> Line: 14
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INTEGER_REDUNDANT_INFERRED" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_INFERRED".
->> WARNING
->> Line: 15
->> UNUSED_LOCAL_CONSTANT
->> The local constant "_INTEGER_REDUNDANT_INFERRED2" is declared but never used in the block. If this is intended, prefix it with an underscore: "__INTEGER_REDUNDANT_INFERRED2".
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_constant.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_constant.gd
new file mode 100644
index 0000000000..3d355197e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_constant.gd
@@ -0,0 +1,4 @@
+func test():
+ const UNUSED = "not used"
+
+ const _UNUSED = "not used, but no warning since the constant name starts with an underscore"
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_constant.out b/modules/gdscript/tests/scripts/parser/warnings/unused_constant.out
new file mode 100644
index 0000000000..99ced48433
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_constant.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant "UNUSED" is declared but never used in the block. If this is intended, prefix it with an underscore: "_UNUSED".
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
index d7485f49e6..42b29eee43 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
@@ -23,6 +23,7 @@ var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
@export var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
+@warning_ignore("enum_variable_without_default")
var test_var_hard_my_enum: MyEnum
var test_var_hard_array: Array
var test_var_hard_array_int: Array[int]
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index f8c35ab6d1..8f0f0d219e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -5685,6 +5685,9 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, const GL
active_skeleton = skeleton;
current_node = active_skeleton;
+ if (active_skeleton) {
+ p_scene_parent = active_skeleton;
+ }
if (requires_extra_node) {
current_node = nullptr;
@@ -7104,6 +7107,9 @@ Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) {
if (p_state->extensions_used.has("GODOT_single_root")) {
_generate_scene_node(p_state, 0, nullptr, nullptr);
single_root = p_state->scene_nodes[0];
+ if (single_root && single_root->get_owner() && single_root->get_owner() != single_root) {
+ single_root = single_root->get_owner();
+ }
} else {
single_root = memnew(Node3D);
for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index b6f5d6ce57..835fb3e59d 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -1908,7 +1908,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
seams_push_constant.slice = uint32_t(i * subslices + k);
seams_push_constant.debug = debug;
- RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
+ RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0);
rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a3464ccfc2..dcc18ebdd7 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -941,6 +941,31 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(scr);
}
+ // Deserialize managed callables.
+ // This is done before reloading script's internal state, so potential callables invoked in properties work.
+ {
+ MutexLock lock(ManagedCallable::instances_mutex);
+
+ for (const KeyValue<ManagedCallable *, Array> &elem : ManagedCallable::instances_pending_reload) {
+ ManagedCallable *managed_callable = elem.key;
+ const Array &serialized_data = elem.value;
+
+ GCHandleIntPtr delegate = { nullptr };
+
+ bool success = GDMonoCache::managed_callbacks.DelegateUtils_TryDeserializeDelegateWithGCHandle(
+ &serialized_data, &delegate);
+
+ if (success) {
+ ERR_CONTINUE(delegate.value == nullptr);
+ managed_callable->delegate_handle = delegate;
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ OS::get_singleton()->print("Failed to deserialize delegate\n");
+ }
+ }
+
+ ManagedCallable::instances_pending_reload.clear();
+ }
+
for (Ref<CSharpScript> &scr : to_reload_state) {
for (const ObjectID &obj_id : scr->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
@@ -963,7 +988,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
properties[G.first] = G.second;
}
- // Restore serialized state and call OnAfterDeserialization
+ // Restore serialized state and call OnAfterDeserialize.
GDMonoCache::managed_callbacks.CSharpInstanceBridge_DeserializeState(
csi->get_gchandle_intptr(), &properties, &state_backup.event_signals);
}
@@ -973,30 +998,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
scr->pending_reload_state.clear();
}
- // Deserialize managed callables
- {
- MutexLock lock(ManagedCallable::instances_mutex);
-
- for (const KeyValue<ManagedCallable *, Array> &elem : ManagedCallable::instances_pending_reload) {
- ManagedCallable *managed_callable = elem.key;
- const Array &serialized_data = elem.value;
-
- GCHandleIntPtr delegate = { nullptr };
-
- bool success = GDMonoCache::managed_callbacks.DelegateUtils_TryDeserializeDelegateWithGCHandle(
- &serialized_data, &delegate);
-
- if (success) {
- ERR_CONTINUE(delegate.value == nullptr);
- managed_callable->delegate_handle = delegate;
- } else if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Failed to deserialize delegate\n");
- }
- }
-
- ManagedCallable::instances_pending_reload.clear();
- }
-
#ifdef TOOLS_ENABLED
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index eb45ade285..e1ce41edd5 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2581,6 +2581,10 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append("\")]");
}
+ if (p_iprop.is_hidden) {
+ p_output.append(MEMBER_BEGIN "[EditorBrowsable(EditorBrowsableState.Never)]");
+ }
+
p_output.append(MEMBER_BEGIN "public ");
if (prop_allowed_inherited_member_hiding.has(p_itype.proxy_name + "." + p_iprop.proxy_name)) {
@@ -2840,7 +2844,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("\")]");
}
- if (p_imethod.is_compat) {
+ if (p_imethod.is_hidden) {
p_output.append(MEMBER_BEGIN "[EditorBrowsable(EditorBrowsableState.Never)]");
}
@@ -3654,11 +3658,16 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname);
- if (iprop.setter != StringName()) {
- accessor_methods[iprop.setter] = iprop.cname;
- }
- if (iprop.getter != StringName()) {
- accessor_methods[iprop.getter] = iprop.cname;
+ // If the property is internal hide it; otherwise, hide the getter and setter.
+ if (property.usage & PROPERTY_USAGE_INTERNAL) {
+ iprop.is_hidden = true;
+ } else {
+ if (iprop.setter != StringName()) {
+ accessor_methods[iprop.setter] = iprop.cname;
+ }
+ if (iprop.getter != StringName()) {
+ accessor_methods[iprop.getter] = iprop.cname;
+ }
}
bool valid = false;
@@ -3860,10 +3869,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
if (accessor) {
- // We only make internal an accessor method if it's in the same class as the property.
+ // We only hide an accessor method if it's in the same class as the property.
// It's easier this way, but also we don't know if an accessor method in a different class
// could have other purposes, so better leave those untouched.
- imethod.is_internal = true;
+ imethod.is_hidden = true;
}
if (itype.class_doc) {
@@ -3892,6 +3901,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// after all the non-compat methods have been added. The compat methods are added in
// reverse so the most recently added ones take precedence over older compat methods.
if (imethod.is_compat) {
+ imethod.is_hidden = true;
compat_methods.push_front(imethod);
continue;
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index bb0ba0cb00..a397dcb026 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -88,6 +88,14 @@ class BindingsGenerator {
StringName setter;
StringName getter;
+ /**
+ * Determines if the property will be hidden with the [EditorBrowsable(EditorBrowsableState.Never)]
+ * attribute.
+ * We do this for propertyies that have the PROPERTY_USAGE_INTERNAL flag, because they are not meant
+ * to be exposed to scripting but we can't remove them to prevent breaking compatibility.
+ */
+ bool is_hidden = false;
+
const DocData::PropertyDoc *prop_doc;
bool is_deprecated = false;
@@ -180,6 +188,14 @@ class BindingsGenerator {
bool is_internal = false;
/**
+ * Determines if the method will be hidden with the [EditorBrowsable(EditorBrowsableState.Never)]
+ * attribute.
+ * We do this for methods that we don't want to expose but need to be public to prevent breaking
+ * compat (i.e: methods with 'is_compat' set to true.)
+ */
+ bool is_hidden = false;
+
+ /**
* Determines if the method is a compatibility method added to avoid breaking binary compatibility.
* These methods will be generated but hidden and are considered deprecated.
*/
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 9cd5498fa8..37f319b697 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1208,39 +1208,39 @@ namespace Godot
/// Do a simple expression match, where '*' matches zero or more
/// arbitrary characters and '?' matches any single character except '.'.
/// </summary>
- /// <param name="instance">The string to check.</param>
- /// <param name="expr">Expression to check.</param>
+ /// <param name="str">The string to check.</param>
+ /// <param name="pattern">Expression to check.</param>
/// <param name="caseSensitive">
/// If <see langword="true"/>, the check will be case sensitive.
/// </param>
/// <returns>If the expression has any matches.</returns>
- private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ private static bool WildcardMatch(ReadOnlySpan<char> str, ReadOnlySpan<char> pattern, bool caseSensitive)
{
// case '\0':
- if (expr.Length == 0)
- return instance.Length == 0;
+ if (pattern.IsEmpty)
+ return str.IsEmpty;
- switch (expr[0])
+ switch (pattern[0])
{
case '*':
- return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
- ExprMatch(instance.Substring(1), expr, caseSensitive));
+ return WildcardMatch(str, pattern.Slice(1), caseSensitive)
+ || (!str.IsEmpty && WildcardMatch(str.Slice(1), pattern, caseSensitive));
case '?':
- return instance.Length > 0 && instance[0] != '.' &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return !str.IsEmpty && str[0] != '.' &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
default:
- if (instance.Length == 0)
+ if (str.IsEmpty)
return false;
- if (caseSensitive)
- return instance[0] == expr[0];
- return (char.ToUpperInvariant(instance[0]) == char.ToUpperInvariant(expr[0])) &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ bool charMatches = caseSensitive ?
+ str[0] == pattern[0] :
+ char.ToUpperInvariant(str[0]) == char.ToUpperInvariant(pattern[0]);
+ return charMatches &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
}
}
/// <summary>
- /// Do a simple case sensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case sensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="MatchN(string, string)"/>
/// <param name="instance">The string to check.</param>
@@ -1254,12 +1254,11 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive);
+ return WildcardMatch(instance, expr, caseSensitive);
}
/// <summary>
- /// Do a simple case insensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case insensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="Match(string, string, bool)"/>
/// <param name="instance">The string to check.</param>
@@ -1270,7 +1269,7 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive: false);
+ return WildcardMatch(instance, expr, caseSensitive: false);
}
/// <summary>
diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp
index 5eefbe4228..bf69adc14c 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.cpp
+++ b/modules/navigation/2d/godot_navigation_server_2d.cpp
@@ -389,7 +389,16 @@ bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid);
void FORWARD_2(agent_set_paused, RID, p_agent, bool, p_paused, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(agent_get_paused, RID, p_agent, rid_to_rid);
-void FORWARD_1(free, RID, p_object, rid_to_rid);
+void GodotNavigationServer2D::free(RID p_object) {
+#ifdef CLIPPER2_ENABLED
+ if (navmesh_generator_2d && navmesh_generator_2d->owns(p_object)) {
+ navmesh_generator_2d->free(p_object);
+ return;
+ }
+#endif // CLIPPER2_ENABLED
+ NavigationServer3D::get_singleton()->free(p_object);
+}
+
void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable);
bool GodotNavigationServer2D::agent_has_avoidance_callback(RID p_agent) const {
return NavigationServer3D::get_singleton()->agent_has_avoidance_callback(p_agent);
@@ -453,3 +462,20 @@ void GodotNavigationServer2D::query_path(const Ref<NavigationPathQueryParameters
p_query_result->set_path_rids(_query_result.path_rids);
p_query_result->set_path_owner_ids(_query_result.path_owner_ids);
}
+
+RID GodotNavigationServer2D::source_geometry_parser_create() {
+#ifdef CLIPPER2_ENABLED
+ if (navmesh_generator_2d) {
+ return navmesh_generator_2d->source_geometry_parser_create();
+ }
+#endif // CLIPPER2_ENABLED
+ return RID();
+}
+
+void GodotNavigationServer2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+#ifdef CLIPPER2_ENABLED
+ if (navmesh_generator_2d) {
+ navmesh_generator_2d->source_geometry_parser_set_callback(p_parser, p_callback);
+ }
+#endif // CLIPPER2_ENABLED
+}
diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h
index ba375afd33..ea77fa5e6e 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.h
+++ b/modules/navigation/2d/godot_navigation_server_2d.h
@@ -253,6 +253,9 @@ public:
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const override;
+ virtual RID source_geometry_parser_create() override;
+ virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override;
+
virtual Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) override;
};
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index d8f1170f6a..13399b858e 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -43,9 +43,9 @@
#include "scene/resources/2d/circle_shape_2d.h"
#include "scene/resources/2d/concave_polygon_shape_2d.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "scene/resources/2d/rectangle_shape_2d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_polygon.h"
#include "thirdparty/clipper2/include/clipper2/clipper.h"
#include "thirdparty/misc/polypartition.h"
@@ -53,11 +53,14 @@
NavMeshGenerator2D *NavMeshGenerator2D::singleton = nullptr;
Mutex NavMeshGenerator2D::baking_navmesh_mutex;
Mutex NavMeshGenerator2D::generator_task_mutex;
+RWLock NavMeshGenerator2D::generator_rid_rwlock;
bool NavMeshGenerator2D::use_threads = true;
bool NavMeshGenerator2D::baking_use_multiple_threads = true;
bool NavMeshGenerator2D::baking_use_high_priority_threads = true;
HashSet<Ref<NavigationPolygon>> NavMeshGenerator2D::baking_navmeshes;
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator2D::NavMeshGeneratorTask2D *> NavMeshGenerator2D::generator_tasks;
+RID_Owner<NavMeshGenerator2D::NavMeshGeometryParser2D> NavMeshGenerator2D::generator_parser_owner;
+LocalVector<NavMeshGenerator2D::NavMeshGeometryParser2D *> NavMeshGenerator2D::generator_parsers;
NavMeshGenerator2D *NavMeshGenerator2D::get_singleton() {
return singleton;
@@ -126,6 +129,13 @@ void NavMeshGenerator2D::cleanup() {
}
generator_tasks.clear();
+ generator_rid_rwlock.write_lock();
+ for (NavMeshGeometryParser2D *parser : generator_parsers) {
+ generator_parser_owner.free(parser->self);
+ }
+ generator_parsers.clear();
+ generator_rid_rwlock.write_unlock();
+
generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
@@ -236,6 +246,15 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_
generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node);
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_rid_rwlock.read_lock();
+ for (const NavMeshGeometryParser2D *parser : generator_parsers) {
+ if (!parser->callback.is_valid()) {
+ continue;
+ }
+ parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node);
+ }
+ generator_rid_rwlock.read_unlock();
+
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
@@ -813,6 +832,47 @@ bool NavMeshGenerator2D::generator_emit_callback(const Callable &p_callback) {
return ce.error == Callable::CallError::CALL_OK;
}
+RID NavMeshGenerator2D::source_geometry_parser_create() {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ RID rid = generator_parser_owner.make_rid();
+
+ NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(rid);
+ parser->self = rid;
+
+ generator_parsers.push_back(parser);
+
+ return rid;
+}
+
+void NavMeshGenerator2D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(p_parser);
+ ERR_FAIL_NULL(parser);
+
+ parser->callback = p_callback;
+}
+
+bool NavMeshGenerator2D::owns(RID p_object) {
+ RWLockRead read_lock(generator_rid_rwlock);
+ return generator_parser_owner.owns(p_object);
+}
+
+void NavMeshGenerator2D::free(RID p_object) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ if (generator_parser_owner.owns(p_object)) {
+ NavMeshGeometryParser2D *parser = generator_parser_owner.get_or_null(p_object);
+
+ generator_parsers.erase(parser);
+
+ generator_parser_owner.free(p_object);
+ } else {
+ ERR_PRINT("Attempted to free a NavMeshGenerator2D RID that did not exist (or was already freed).");
+ }
+}
+
void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data) {
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
return;
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.h b/modules/navigation/2d/nav_mesh_generator_2d.h
index 2567a170ef..235a84d548 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.h
+++ b/modules/navigation/2d/nav_mesh_generator_2d.h
@@ -35,6 +35,7 @@
#include "core/object/class_db.h"
#include "core/object/worker_thread_pool.h"
+#include "core/templates/rid_owner.h"
class Node;
class NavigationPolygon;
@@ -46,6 +47,14 @@ class NavMeshGenerator2D : public Object {
static Mutex baking_navmesh_mutex;
static Mutex generator_task_mutex;
+ static RWLock generator_rid_rwlock;
+ struct NavMeshGeometryParser2D {
+ RID self;
+ Callable callback;
+ };
+ static RID_Owner<NavMeshGeometryParser2D> generator_parser_owner;
+ static LocalVector<NavMeshGeometryParser2D *> generator_parsers;
+
static bool use_threads;
static bool baking_use_multiple_threads;
static bool baking_use_high_priority_threads;
@@ -97,6 +106,12 @@ public:
static void bake_from_source_geometry_data_async(Ref<NavigationPolygon> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, const Callable &p_callback = Callable());
static bool is_baking(Ref<NavigationPolygon> p_navigation_polygon);
+ static RID source_geometry_parser_create();
+ static void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback);
+
+ static bool owns(RID p_object);
+ static void free(RID p_object);
+
NavMeshGenerator2D();
~NavMeshGenerator2D();
};
diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp
index 301b4aa8f3..61a128e004 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.cpp
+++ b/modules/navigation/3d/godot_navigation_server_3d.cpp
@@ -1202,6 +1202,11 @@ COMMAND_1(free, RID, p_object) {
} else if (obstacle_owner.owns(p_object)) {
internal_free_obstacle(p_object);
+#ifndef _3D_DISABLED
+ } else if (navmesh_generator_3d && navmesh_generator_3d->owns(p_object)) {
+ navmesh_generator_3d->free(p_object);
+#endif // _3D_DISABLED
+
} else {
ERR_PRINT("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
}
@@ -1428,6 +1433,23 @@ PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters &
return r_query_result;
}
+RID GodotNavigationServer3D::source_geometry_parser_create() {
+#ifndef _3D_DISABLED
+ if (navmesh_generator_3d) {
+ return navmesh_generator_3d->source_geometry_parser_create();
+ }
+#endif // _3D_DISABLED
+ return RID();
+}
+
+void GodotNavigationServer3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+#ifndef _3D_DISABLED
+ if (navmesh_generator_3d) {
+ navmesh_generator_3d->source_geometry_parser_set_callback(p_parser, p_callback);
+ }
+#endif // _3D_DISABLED
+}
+
Vector<Vector3> GodotNavigationServer3D::simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) {
if (p_path.size() <= 2) {
return p_path;
diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h
index 89839ff459..5ba7ed1088 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.h
+++ b/modules/navigation/3d/godot_navigation_server_3d.h
@@ -264,6 +264,9 @@ public:
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override;
+ virtual RID source_geometry_parser_create() override;
+ virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override;
+
virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override;
private:
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index 3d3f4b4679..cc3bbdbf01 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -45,12 +45,12 @@
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/shape_3d.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/3d/world_boundary_shape_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -66,11 +66,14 @@
NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr;
Mutex NavMeshGenerator3D::baking_navmesh_mutex;
Mutex NavMeshGenerator3D::generator_task_mutex;
+RWLock NavMeshGenerator3D::generator_rid_rwlock;
bool NavMeshGenerator3D::use_threads = true;
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
+RID_Owner<NavMeshGenerator3D::NavMeshGeometryParser3D> NavMeshGenerator3D::generator_parser_owner;
+LocalVector<NavMeshGenerator3D::NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
return singleton;
@@ -139,6 +142,13 @@ void NavMeshGenerator3D::cleanup() {
}
generator_tasks.clear();
+ generator_rid_rwlock.write_lock();
+ for (NavMeshGeometryParser3D *parser : generator_parsers) {
+ generator_parser_owner.free(parser->self);
+ }
+ generator_parsers.clear();
+ generator_rid_rwlock.write_unlock();
+
generator_task_mutex.unlock();
baking_navmesh_mutex.unlock();
}
@@ -254,6 +264,15 @@ void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh>
#endif
generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node);
+ generator_rid_rwlock.read_lock();
+ for (const NavMeshGeometryParser3D *parser : generator_parsers) {
+ if (!parser->callback.is_valid()) {
+ continue;
+ }
+ parser->callback.call(p_navigation_mesh, p_source_geometry_data, p_node);
+ }
+ generator_rid_rwlock.read_unlock();
+
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children);
@@ -920,4 +939,45 @@ bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
return ce.error == Callable::CallError::CALL_OK;
}
+RID NavMeshGenerator3D::source_geometry_parser_create() {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ RID rid = generator_parser_owner.make_rid();
+
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(rid);
+ parser->self = rid;
+
+ generator_parsers.push_back(parser);
+
+ return rid;
+}
+
+void NavMeshGenerator3D::source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_parser);
+ ERR_FAIL_NULL(parser);
+
+ parser->callback = p_callback;
+}
+
+bool NavMeshGenerator3D::owns(RID p_object) {
+ RWLockRead read_lock(generator_rid_rwlock);
+ return generator_parser_owner.owns(p_object);
+}
+
+void NavMeshGenerator3D::free(RID p_object) {
+ RWLockWrite write_lock(generator_rid_rwlock);
+
+ if (generator_parser_owner.owns(p_object)) {
+ NavMeshGeometryParser3D *parser = generator_parser_owner.get_or_null(p_object);
+
+ generator_parsers.erase(parser);
+
+ generator_parser_owner.free(p_object);
+ } else {
+ ERR_PRINT("Attempted to free a NavMeshGenerator3D RID that did not exist (or was already freed).");
+ }
+}
+
#endif // _3D_DISABLED
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.h b/modules/navigation/3d/nav_mesh_generator_3d.h
index 9c9b3bdefe..b46a1736e0 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.h
+++ b/modules/navigation/3d/nav_mesh_generator_3d.h
@@ -35,6 +35,7 @@
#include "core/object/class_db.h"
#include "core/object/worker_thread_pool.h"
+#include "core/templates/rid_owner.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
class Node;
@@ -47,6 +48,14 @@ class NavMeshGenerator3D : public Object {
static Mutex baking_navmesh_mutex;
static Mutex generator_task_mutex;
+ static RWLock generator_rid_rwlock;
+ struct NavMeshGeometryParser3D {
+ RID self;
+ Callable callback;
+ };
+ static RID_Owner<NavMeshGeometryParser3D> generator_parser_owner;
+ static LocalVector<NavMeshGeometryParser3D *> generator_parsers;
+
static bool use_threads;
static bool baking_use_multiple_threads;
static bool baking_use_high_priority_threads;
@@ -102,6 +111,12 @@ public:
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
static bool is_baking(Ref<NavigationMesh> p_navigation_mesh);
+ static RID source_geometry_parser_create();
+ static void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback);
+
+ static bool owns(RID p_object);
+ static void free(RID p_object);
+
NavMeshGenerator3D();
~NavMeshGenerator3D();
};
diff --git a/modules/navigation/3d/navigation_mesh_generator.cpp b/modules/navigation/3d/navigation_mesh_generator.cpp
index 8393896db1..54df42e266 100644
--- a/modules/navigation/3d/navigation_mesh_generator.cpp
+++ b/modules/navigation/3d/navigation_mesh_generator.cpp
@@ -32,7 +32,7 @@
#include "navigation_mesh_generator.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index 352203f5aa..d7bf1cdd38 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -42,7 +42,6 @@
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
void NavigationMeshEditor::_node_removed(Node *p_node) {
if (p_node == node) {
diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
index 168e0bf077..b9c69075e1 100644
--- a/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
+++ b/modules/openxr/doc_classes/OpenXRCompositionLayer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRCompositionLayer" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRCompositionLayer" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The parent class of all OpenXR composition layer nodes.
</brief_description>
diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml
index 2de1977671..dd8a11e2b9 100644
--- a/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml
+++ b/modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a cylinder.
</brief_description>
diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml
index f6eba7e228..716ea72854 100644
--- a/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml
+++ b/modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a sphere.
</brief_description>
diff --git a/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml b/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml
index fff592bc4f..6632f90ed2 100644
--- a/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml
+++ b/modules/openxr/doc_classes/OpenXRCompositionLayerQuad.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as a quad.
</brief_description>
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
index 9cc548dd6f..23d932ddd7 100644
--- a/modules/openxr/doc_classes/OpenXRHand.xml
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRHand" inherits="SkeletonModifier3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
@@ -18,11 +18,14 @@
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
+ <member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
+ Set a [Skeleton3D] node for which the pose positions will be updated.
+ </member>
<member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
Set the motion range (if supported) limiting the hand motion.
</member>
<member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0">
- Set the type of skeleton rig the parent [Skeleton3D] is compliant with.
+ Set the type of skeleton rig the [member hand_skeleton] is compliant with.
</member>
</members>
<constants>
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 1136ac1b69..05dff7d6ae 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -23,7 +23,7 @@
Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized.
</description>
</method>
- <method name="get_hand_joint_angular_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_angular_velocity] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_angular_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_angular_velocity] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -31,7 +31,7 @@
If handtracking is enabled, returns the angular velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D]!
</description>
</method>
- <method name="get_hand_joint_flags" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_flags] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_flags" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_flags] obtained from [method XRServer.get_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandJointFlags" is_bitfield="true" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -39,7 +39,7 @@
If handtracking is enabled, returns flags that inform us of the validity of the tracking data.
</description>
</method>
- <method name="get_hand_joint_linear_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_linear_velocity] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_linear_velocity" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_linear_velocity] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -47,7 +47,7 @@
If handtracking is enabled, returns the linear velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_position" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_position" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_tracker] instead.">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -55,7 +55,7 @@
If handtracking is enabled, returns the position of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_radius" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_radius] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_radius" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_radius] obtained from [method XRServer.get_tracker] instead.">
<return type="float" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -63,7 +63,7 @@
If handtracking is enabled, returns the radius of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is without worldscale applied!
</description>
</method>
- <method name="get_hand_joint_rotation" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_joint_rotation" qualifiers="const" deprecated="Use [method XRHandTracker.get_hand_joint_transform] obtained from [method XRServer.get_tracker] instead.">
<return type="Quaternion" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
@@ -71,7 +71,7 @@
If handtracking is enabled, returns the rotation of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR.
</description>
</method>
- <method name="get_hand_tracking_source" qualifiers="const" deprecated="Use [member XRHandTracker.hand_tracking_source] obtained from [method XRServer.get_hand_tracker] instead.">
+ <method name="get_hand_tracking_source" qualifiers="const" deprecated="Use [member XRHandTracker.hand_tracking_source] obtained from [method XRServer.get_tracker] instead.">
<return type="int" enum="OpenXRInterface.HandTrackedSource" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
<description>
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index b3c20ef8b9..f8cc3d1d8c 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -196,7 +196,8 @@ void OpenXRHandTrackingExtension::on_process() {
Ref<XRHandTracker> godot_tracker;
godot_tracker.instantiate();
godot_tracker->set_hand(i == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT);
- XRServer::get_singleton()->add_hand_tracker(i == 0 ? "/user/left" : "/user/right", godot_tracker);
+ godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right");
+ XRServer::get_singleton()->add_tracker(godot_tracker);
hand_trackers[i].godot_tracker = godot_tracker;
hand_trackers[i].is_initialized = true;
@@ -229,8 +230,7 @@ void OpenXRHandTrackingExtension::on_process() {
// For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large
const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose;
- if (
- !hand_trackers[i].locations.isActive || isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
+ if (!hand_trackers[i].locations.isActive || isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
}
@@ -249,6 +249,8 @@ void OpenXRHandTrackingExtension::on_process() {
const XrPosef &pose = location.pose;
Transform3D transform;
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
BitField<XRHandTracker::HandJointFlags> flags;
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
@@ -269,27 +271,34 @@ void OpenXRHandTrackingExtension::on_process() {
}
if (location.locationFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
- godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z));
+ linear_velocity = Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
+ godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, linear_velocity);
}
if (location.locationFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
- godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z));
+ angular_velocity = Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
+ godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, angular_velocity);
}
godot_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)joint, flags);
godot_tracker->set_hand_joint_transform((XRHandTracker::HandJoint)joint, transform);
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
- 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;
+ 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);
+ godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
}
- godot_tracker->set_hand_tracking_source(source);
}
} else {
godot_tracker->set_has_tracking_data(false);
+ godot_tracker->invalidate_pose("default");
}
}
}
@@ -311,7 +320,7 @@ void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
hand_trackers[i].is_initialized = false;
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
- XRServer::get_singleton()->remove_hand_tracker(i == 0 ? "/user/left" : "/user/right");
+ XRServer::get_singleton()->remove_tracker(hand_trackers[i].godot_tracker);
}
}
}
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 7eb9a6ebe1..aa68441f03 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -35,6 +35,7 @@
#include "servers/rendering/rendering_server_globals.h"
#include "extensions/openxr_eye_gaze_interaction.h"
+#include "thirdparty/openxr/include/openxr/openxr.h"
void OpenXRInterface::_bind_methods() {
// lifecycle signals
@@ -154,9 +155,14 @@ PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {
// These are hardcoded in OpenXR, note that they will only be available if added to our action map
PackedStringArray arr = {
- "left_hand", // /user/hand/left is mapped to our defaults
- "right_hand", // /user/hand/right is mapped to our defaults
- "/user/treadmill",
+ "head", // XRPositionalTracker for the users head (Mapped from OpenXR /user/head)
+ "left_hand", // XRControllerTracker for the users left hand (Mapped from OpenXR /user/hand/left)
+ "right_hand", // XRControllerTracker for the users right hand (Mapped from OpenXR /user/hand/right)
+ "/user/hand_tracker/left", // XRHandTracker for the users left hand
+ "/user/hand_tracker/right", // XRHandTracker for the users right hand
+ "/user/body_tracker", // XRBodyTracker for the users body
+ "/user/face_tracker", // XRFaceTracker for the users face
+ "/user/treadmill"
};
for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {
@@ -430,34 +436,31 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_
RID tracker_rid = openxr_api->tracker_create(p_tracker_name);
ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);
- // create our positional tracker
- Ref<XRPositionalTracker> positional_tracker;
- positional_tracker.instantiate();
+ // Create our controller tracker.
+ Ref<XRControllerTracker> controller_tracker;
+ controller_tracker.instantiate();
// We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these.
if (p_tracker_name == "/user/hand/left") {
- positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- positional_tracker->set_tracker_name("left_hand");
- positional_tracker->set_tracker_desc("Left hand controller");
- positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);
+ controller_tracker->set_tracker_name("left_hand");
+ controller_tracker->set_tracker_desc("Left hand controller");
+ controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);
} else if (p_tracker_name == "/user/hand/right") {
- positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- positional_tracker->set_tracker_name("right_hand");
- positional_tracker->set_tracker_desc("Right hand controller");
- positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);
+ controller_tracker->set_tracker_name("right_hand");
+ controller_tracker->set_tracker_desc("Right hand controller");
+ controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);
} else {
- positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- positional_tracker->set_tracker_name(p_tracker_name);
- positional_tracker->set_tracker_desc(p_tracker_name);
+ controller_tracker->set_tracker_name(p_tracker_name);
+ controller_tracker->set_tracker_desc(p_tracker_name);
}
- positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
- xr_server->add_tracker(positional_tracker);
+ controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
+ xr_server->add_tracker(controller_tracker);
// create a new entry
tracker = memnew(Tracker);
tracker->tracker_name = p_tracker_name;
tracker->tracker_rid = tracker_rid;
- tracker->positional_tracker = positional_tracker;
+ tracker->controller_tracker = controller_tracker;
tracker->interaction_profile = RID();
trackers.push_back(tracker);
@@ -477,17 +480,17 @@ void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_p
if (p_interaction_profile.is_null()) {
print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + INTERACTION_PROFILE_NONE);
- tracker->positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
+ tracker->controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
} else {
String name = openxr_api->interaction_profile_get_name(p_interaction_profile);
print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + name);
- tracker->positional_tracker->set_tracker_profile(name);
+ tracker->controller_tracker->set_tracker_profile(name);
}
}
void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
ERR_FAIL_NULL(openxr_api);
- ERR_FAIL_COND(p_tracker->positional_tracker.is_null());
+ ERR_FAIL_COND(p_tracker->controller_tracker.is_null());
// Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction
// profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these
@@ -506,15 +509,15 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
switch (action->action_type) {
case OpenXRAction::OPENXR_ACTION_BOOL: {
bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid);
- p_tracker->positional_tracker->set_input(action->action_name, Variant(pressed));
+ p_tracker->controller_tracker->set_input(action->action_name, Variant(pressed));
} break;
case OpenXRAction::OPENXR_ACTION_FLOAT: {
real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid);
- p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
+ p_tracker->controller_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid);
- p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
+ p_tracker->controller_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_POSE: {
Transform3D transform;
@@ -523,9 +526,9 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular);
if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
- p_tracker->positional_tracker->set_pose(action->action_name, transform, linear, angular, confidence);
+ p_tracker->controller_tracker->set_pose(action->action_name, transform, linear, angular, confidence);
} else {
- p_tracker->positional_tracker->invalidate_pose(action->action_name);
+ p_tracker->controller_tracker->invalidate_pose(action->action_name);
}
} break;
default: {
@@ -567,8 +570,8 @@ void OpenXRInterface::free_trackers() {
Tracker *tracker = trackers[i];
openxr_api->tracker_free(tracker->tracker_rid);
- xr_server->remove_tracker(tracker->positional_tracker);
- tracker->positional_tracker.unref();
+ xr_server->remove_tracker(tracker->controller_tracker);
+ tracker->controller_tracker.unref();
memdelete(tracker);
}
@@ -1005,7 +1008,7 @@ void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrack
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
OpenXRInterface::Tracker *tracker = find_tracker(p_path);
- if (tracker && tracker->positional_tracker.is_valid()) {
+ if (tracker && tracker->controller_tracker.is_valid()) {
XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);
if (location_flags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT)) {
@@ -1035,9 +1038,9 @@ void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrack
angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
}
- tracker->positional_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);
+ tracker->controller_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);
} else {
- tracker->positional_tracker->invalidate_pose("skeleton");
+ tracker->controller_tracker->invalidate_pose("skeleton");
}
}
}
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index 737f22d642..e916c7dac2 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -35,8 +35,8 @@
#include "extensions/openxr_hand_tracking_extension.h"
#include "openxr_api.h"
+#include "servers/xr/xr_controller_tracker.h"
#include "servers/xr/xr_interface.h"
-#include "servers/xr/xr_positional_tracker.h"
// declare some default strings
#define INTERACTION_PROFILE_NONE "/interaction_profiles/none"
@@ -73,7 +73,7 @@ private:
struct Tracker { // A tracker we've registered with OpenXR
String tracker_name; // Name of our tracker (can be altered from the action map)
Vector<Action *> actions; // Actions related to this tracker
- Ref<XRPositionalTracker> positional_tracker; // Our positional tracker object that holds our tracker state
+ Ref<XRControllerTracker> controller_tracker; // Our positional tracker object that holds our tracker state
RID tracker_rid; // RID of the tracker registered with our OpenXR API
RID interaction_profile; // RID of the interaction profile bound to this tracker (can be null)
};
diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp
index f20d1f8e19..2a4104f6ee 100644
--- a/modules/openxr/scene/openxr_hand.cpp
+++ b/modules/openxr/scene/openxr_hand.cpp
@@ -40,6 +40,9 @@ void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
+ ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
+ ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
+
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
@@ -51,6 +54,7 @@ void OpenXRHand::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
@@ -86,6 +90,12 @@ OpenXRHand::Hands OpenXRHand::get_hand() const {
return hand;
}
+void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
+ hand_skeleton = p_hand_skeleton;
+
+ // TODO if inside tree call _get_bones()
+}
+
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
motion_range = p_motion_range;
@@ -97,6 +107,10 @@ OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
return motion_range;
}
+NodePath OpenXRHand::get_hand_skeleton() const {
+ return hand_skeleton;
+}
+
void OpenXRHand::_set_motion_range() {
if (!hand_tracking_ext) {
return;
@@ -138,6 +152,20 @@ OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
return bone_update;
}
+Skeleton3D *OpenXRHand::get_skeleton() {
+ if (!has_node(hand_skeleton)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(hand_skeleton);
+ if (!node) {
+ return nullptr;
+ }
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ return skeleton;
+}
+
void OpenXRHand::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
@@ -262,7 +290,7 @@ void OpenXRHand::_get_joint_data() {
}
}
-void OpenXRHand::_process_modification() {
+void OpenXRHand::_update_skeleton() {
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
return;
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
@@ -367,14 +395,21 @@ void OpenXRHand::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_get_joint_data();
+
+ set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
+ set_process_internal(false);
+
// reset
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_skeleton();
+ } break;
default: {
} break;
}
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
index fc0a994f48..4c77e7277c 100644
--- a/modules/openxr/scene/openxr_hand.h
+++ b/modules/openxr/scene/openxr_hand.h
@@ -31,15 +31,16 @@
#ifndef OPENXR_HAND_H
#define OPENXR_HAND_H
-#include "scene/3d/skeleton_modifier_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRHandTrackingExtension;
-class OpenXRHand : public SkeletonModifier3D {
- GDCLASS(OpenXRHand, SkeletonModifier3D);
+class OpenXRHand : public Node3D {
+ GDCLASS(OpenXRHand, Node3D);
public:
enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands.
@@ -85,13 +86,13 @@ private:
void _set_motion_range();
+ Skeleton3D *get_skeleton();
void _get_joint_data();
+ void _update_skeleton();
protected:
static void _bind_methods();
- virtual void _process_modification() override;
-
public:
OpenXRHand();
@@ -101,6 +102,9 @@ public:
void set_motion_range(MotionRange p_motion_range);
MotionRange get_motion_range() const;
+ void set_hand_skeleton(const NodePath &p_hand_skeleton);
+ NodePath get_hand_skeleton() const;
+
void set_skeleton_rig(SkeletonRig p_skeleton_rig);
SkeletonRig get_skeleton_rig() const;
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index caf7958f6b..9fd4511d2b 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -114,10 +114,10 @@
</description>
</method>
<method name="get_input_source_tracker" qualifiers="const">
- <return type="XRPositionalTracker" />
+ <return type="XRControllerTracker" />
<param index="0" name="input_source_id" type="int" />
<description>
- Gets an [XRPositionalTracker] for the given [param input_source_id].
+ Gets an [XRControllerTracker] for the given [param input_source_id].
In the context of WebXR, an input source can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional input source is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
Use this method to get information about the input source that triggered one of these signals:
- [signal selectstart]
diff --git a/modules/webxr/webxr_interface.compat.inc b/modules/webxr/webxr_interface.compat.inc
new file mode 100644
index 0000000000..97a9d44ca9
--- /dev/null
+++ b/modules/webxr/webxr_interface.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* webxr_interface.compat.inc */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+Ref<XRPositionalTracker> WebXRInterface::_get_input_source_tracker_bind_compat_90645(int p_input_source_id) const {
+ return get_input_source_tracker(p_input_source_id);
+}
+
+void WebXRInterface::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::_get_input_source_tracker_bind_compat_90645);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp
index c3efebef0f..4795fcdcd6 100644
--- a/modules/webxr/webxr_interface.cpp
+++ b/modules/webxr/webxr_interface.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "webxr_interface.h"
+#include "webxr_interface.compat.inc"
#include <stdlib.h>
diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h
index 06c18d0486..241dc9fe76 100644
--- a/modules/webxr/webxr_interface.h
+++ b/modules/webxr/webxr_interface.h
@@ -31,8 +31,8 @@
#ifndef WEBXR_INTERFACE_H
#define WEBXR_INTERFACE_H
+#include "servers/xr/xr_controller_tracker.h"
#include "servers/xr/xr_interface.h"
-#include "servers/xr/xr_positional_tracker.h"
/**
The WebXR interface is a VR/AR interface that can be used on the web.
@@ -44,6 +44,11 @@ class WebXRInterface : public XRInterface {
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ static void _bind_compatibility_methods();
+ Ref<XRPositionalTracker> _get_input_source_tracker_bind_compat_90645(int p_input_source_id) const;
+#endif
+
public:
enum TargetRayMode {
TARGET_RAY_MODE_UNKNOWN,
@@ -64,7 +69,7 @@ public:
virtual String get_reference_space_type() const = 0;
virtual String get_enabled_features() const = 0;
virtual bool is_input_source_active(int p_input_source_id) const = 0;
- virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
+ virtual Ref<XRControllerTracker> get_input_source_tracker(int p_input_source_id) const = 0;
virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
virtual String get_visibility_state() const = 0;
virtual float get_display_refresh_rate() const = 0;
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index c6213d1aae..535d464d6f 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -164,8 +164,8 @@ bool WebXRInterfaceJS::is_input_source_active(int p_input_source_id) const {
return input_sources[p_input_source_id].active;
}
-Ref<XRPositionalTracker> WebXRInterfaceJS::get_input_source_tracker(int p_input_source_id) const {
- ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, Ref<XRPositionalTracker>());
+Ref<XRControllerTracker> WebXRInterfaceJS::get_input_source_tracker(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, Ref<XRControllerTracker>());
return input_sources[p_input_source_id].tracker;
}
@@ -307,7 +307,7 @@ void WebXRInterfaceJS::uninitialize() {
for (int i = 0; i < HAND_MAX; i++) {
if (hand_trackers[i].is_valid()) {
- xr_server->remove_hand_tracker(i == 0 ? "/user/left" : "/user/right");
+ xr_server->remove_tracker(hand_trackers[i]);
hand_trackers[i].unref();
}
@@ -616,7 +616,7 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
input_source.target_ray_mode = (WebXRInterface::TargetRayMode)tmp_target_ray_mode;
input_source.touch_index = touch_index;
- Ref<XRPositionalTracker> &tracker = input_source.tracker;
+ Ref<XRControllerTracker> &tracker = input_source.tracker;
if (tracker.is_null()) {
tracker.instantiate();
@@ -630,7 +630,6 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
// Input source id's 0 and 1 are always the left and right hands.
if (p_input_source_id < 2) {
- tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
tracker->set_tracker_name(tracker_name);
tracker->set_tracker_desc(p_input_source_id == 0 ? "Left hand controller" : "Right hand controller");
tracker->set_tracker_hand(p_input_source_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
@@ -715,6 +714,7 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
if (unlikely(hand_tracker.is_null())) {
hand_tracker.instantiate();
hand_tracker->set_hand(p_input_source_id == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT);
+ hand_tracker->set_tracker_name(p_input_source_id == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right");
// These flags always apply, since WebXR doesn't give us enough insight to be more fine grained.
BitField<XRHandTracker::HandJointFlags> joint_flags(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID | XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID | XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED | XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED);
@@ -723,7 +723,7 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
}
hand_trackers[p_input_source_id] = hand_tracker;
- xr_server->add_hand_tracker(p_input_source_id == 0 ? "/user/left" : "/user/right", hand_tracker);
+ xr_server->add_tracker(hand_tracker);
}
hand_tracker->set_has_tracking_data(true);
@@ -746,10 +746,12 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
Transform3D palm_transform;
palm_transform.origin = (Vector3(start_pos[0], start_pos[1], start_pos[2]) + Vector3(end_pos[0], end_pos[1], end_pos[2])) / 2.0;
hand_tracker->set_hand_joint_transform(XRHandTracker::HAND_JOINT_PALM, palm_transform);
+ hand_tracker->set_pose("default", palm_transform, Vector3(), Vector3());
}
} else if (hand_tracker.is_valid()) {
hand_tracker->set_has_tracking_data(false);
+ hand_tracker->invalidate_pose("default");
}
}
}
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index fc5df3a59b..afce28d410 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -33,6 +33,8 @@
#ifdef WEB_ENABLED
+#include "servers/xr/xr_controller_tracker.h"
+#include "servers/xr/xr_hand_tracker.h"
#include "webxr_interface.h"
/**
@@ -68,7 +70,7 @@ private:
static constexpr uint8_t input_source_count = 16;
struct InputSource {
- Ref<XRPositionalTracker> tracker;
+ Ref<XRControllerTracker> tracker;
bool active = false;
TargetRayMode target_ray_mode;
int touch_index = -1;
@@ -102,7 +104,7 @@ public:
virtual String get_reference_space_type() const override;
virtual String get_enabled_features() const override;
virtual bool is_input_source_active(int p_input_source_id) const override;
- virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const override;
+ virtual Ref<XRControllerTracker> get_input_source_tracker(int p_input_source_id) const override;
virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const override;
virtual String get_visibility_state() const override;
virtual PackedVector3Array get_play_area() const override;