diff options
Diffstat (limited to 'modules')
55 files changed, 1058 insertions, 513 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index cd837b3a91..32acad76aa 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -227,18 +227,6 @@ [/codeblock] </description> </method> - <method name="str" qualifiers="vararg"> - <return type="String" /> - <description> - Converts one or more arguments to a [String] in the best way possible. - [codeblock] - var a = [10, 20, 30] - var b = str(a); - len(a) # Returns 3 - len(b) # Returns 12 - [/codeblock] - </description> - </method> <method name="type_exists"> <return type="bool" /> <param index="0" name="type" type="StringName" /> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1c2b743909..fd04d3c660 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2240,6 +2240,28 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) { push_error("Cannot assign a new value to a constant.", p_assignment->assignee); + return; + } else if (assignee_type.is_read_only) { + push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee); + return; + } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) { + GDScriptParser::SubscriptNode *sub = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee); + while (sub) { + const GDScriptParser::DataType &base_type = sub->base->datatype; + if (base_type.is_hard_type() && base_type.is_read_only) { + if (base_type.kind == GDScriptParser::DataType::BUILTIN && !Variant::is_type_shared(base_type.builtin_type)) { + push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee); + return; + } + } else { + break; + } + if (sub->base->type == GDScriptParser::Node::SUBSCRIPT) { + sub = static_cast<GDScriptParser::SubscriptNode *>(sub->base); + } else { + sub = nullptr; + } + } } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. @@ -3329,7 +3351,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod StringName getter_name = ClassDB::get_property_getter(native, name); MethodBind *getter = ClassDB::get_method(native, getter_name); if (getter != nullptr) { - p_identifier->set_datatype(type_from_property(getter->get_return_info())); + bool has_setter = ClassDB::get_property_setter(native, name) != StringName(); + p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter)); p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE; } return; @@ -4037,6 +4060,10 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) { Variant value; + if (p_expression == nullptr) { + return value; + } + if (p_expression->is_constant) { is_reduced = true; value = p_expression->reduced_value; @@ -4101,6 +4128,10 @@ Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::Dictiona } Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced) { + if (p_subscript->base == nullptr || p_subscript->index == nullptr) { + return Variant(); + } + bool is_base_value_reduced = false; Variant base_value = make_expression_reduced_value(p_subscript->base, is_base_value_reduced); if (!is_base_value_reduced) { @@ -4260,8 +4291,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars return result; } -GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg) const { +GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg, bool p_is_readonly) const { GDScriptParser::DataType result; + result.is_read_only = p_is_readonly; result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; if (p_property.type == Variant::NIL && (p_is_arg || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) { // Variant diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index b51564fb0a..75d52509a4 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -115,7 +115,7 @@ class GDScriptAnalyzer { Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr); GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type); - GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const; + GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false, bool p_is_readonly = false) const; GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source); bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg); diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index b5c8a6f478..d4f4358ac1 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -317,7 +317,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { text += " = "; text += DADDR(2); - incr += 3; + incr += 6; } break; case OPCODE_ASSIGN_TYPED_NATIVE: { text += "assign typed native ("; @@ -434,7 +434,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { int instr_var_args = _code_ptr[++ip]; int argc = _code_ptr[ip + 1 + instr_var_args]; - Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2]); + Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2] & GDScriptFunction::ADDR_MASK); Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + argc + 4]; StringName native_type = get_global_name(_code_ptr[ip + argc + 5]); @@ -463,7 +463,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { text += "]"; - incr += 3 + argc; + incr += 6 + argc; } break; case OPCODE_CONSTRUCT_DICTIONARY: { int instr_var_args = _code_ptr[++ip]; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index f88ac581ca..12c10642ec 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1953,17 +1953,19 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, case GDScriptParser::DataType::CLASS: if (base_type.class_type->has_function(p_context.current_function->identifier->name)) { GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function; - const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]]; - if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) { - id_type = parameter->get_datatype(); - } - if (parameter->initializer) { - GDScriptParser::CompletionContext c = p_context; - c.current_function = parent_function; - c.current_class = base_type.class_type; - c.base = nullptr; - if (_guess_expression_type(c, parameter->initializer, r_type)) { - return true; + if (parent_function->parameters_indices.has(p_identifier)) { + const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]]; + if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) { + id_type = parameter->get_datatype(); + } + if (parameter->initializer) { + GDScriptParser::CompletionContext c = p_context; + c.current_function = parent_function; + c.current_class = base_type.class_type; + c.base = nullptr; + if (_guess_expression_type(c, parameter->initializer, r_type)) { + return true; + } } } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 713ad3ed17..ed2dce471f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3602,6 +3602,7 @@ bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_node) { ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)"); + ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false); ClassNode *p_class = static_cast<ClassNode *>(p_node); p_class->icon_path = p_annotation->resolved_arguments[0]; return true; @@ -3830,6 +3831,10 @@ template <PropertyUsageFlags t_usage> bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) { AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation); + if (annotation->resolved_arguments.is_empty()) { + return false; + } + annotation->export_info.name = annotation->resolved_arguments[0]; switch (t_usage) { @@ -3887,7 +3892,7 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_ Dictionary rpc_config; rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY; - if (p_annotation->resolved_arguments.size()) { + if (!p_annotation->resolved_arguments.is_empty()) { int last = p_annotation->resolved_arguments.size() - 1; if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) { rpc_config["channel"] = p_annotation->resolved_arguments[last].operator int(); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 07dac25ec5..bc0fe58fa7 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -122,6 +122,7 @@ public: TypeSource type_source = UNDETECTED; bool is_constant = false; + bool is_read_only = false; bool is_meta_type = false; bool is_coroutine = false; // For function calls. @@ -206,6 +207,7 @@ public: void operator=(const DataType &p_other) { kind = p_other.kind; type_source = p_other.type_source; + is_read_only = p_other.is_read_only; is_constant = p_other.is_constant; is_meta_type = p_other.is_meta_type; is_coroutine = p_other.is_coroutine; diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index 10d83dcfe5..758b61bb31 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -112,28 +112,6 @@ struct GDScriptUtilityFunctionsDefinitions { *r_ret = String(result); } - static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - r_error.expected = 1; - *r_ret = Variant(); - return; - } - - String str; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - str = os; - } else { - str += os; - } - } - *r_ret = str; - } - static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { switch (p_arg_count) { case 0: { @@ -651,7 +629,6 @@ void GDScriptUtilityFunctions::register_functions() { REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT)); REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME)); REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT)); - REGISTER_VARARG_FUNC(str, true, Variant::STRING); REGISTER_VARARG_FUNC(range, false, Variant::ARRAY); REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING)); REGISTER_FUNC(inst_to_dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT)); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index e18a4a6190..b99f5d2685 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -811,13 +811,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { + Object *obj = dst->get_validated_object(); String v = index->operator String(); - if (!v.is_empty()) { - v = "'" + v + "'"; + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), v) && (ClassDB::get_property_setter(obj->get_class_name(), v) == StringName()); + } + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", v, _get_var_type(dst)); } else { - v = "of type '" + _get_var_type(index) + "'"; + if (!v.is_empty()) { + v = "'" + v + "'"; + } else { + v = "of type '" + _get_var_type(index) + "'"; + } + err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; } - err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; OPCODE_BREAK; } #endif @@ -1003,8 +1012,16 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED if (!valid) { - String err_type; - err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + Object *obj = dst->get_validated_object(); + bool read_only_property = false; + if (obj) { + read_only_property = ClassDB::has_property(obj->get_class_name(), *index) && (ClassDB::get_property_setter(obj->get_class_name(), *index) == StringName()); + } + if (read_only_property) { + err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst)); + } else { + err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + } OPCODE_BREAK; } #endif diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd new file mode 100644 index 0000000000..2b1c4c9594 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.gd @@ -0,0 +1,4 @@ +func test(): + var tree := SceneTree.new() + tree.root = Window.new() + tree.free() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out new file mode 100644 index 0000000000..b236d70ec8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a read-only property. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd new file mode 100644 index 0000000000..c97ee0ea69 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.gd @@ -0,0 +1,4 @@ +func test(): + var state := PhysicsDirectBodyState3DExtension.new() + state.center_of_mass.x += 1.0 + state.free() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out new file mode 100644 index 0000000000..b236d70ec8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_to_read_only_property_indirectly.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a new value to a read-only property. diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd new file mode 100644 index 0000000000..19c4186622 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.gd @@ -0,0 +1,7 @@ +func test(): + var state = PhysicsDirectBodyState3DExtension.new() + assign(state) + state.free() + +func assign(state): + state.center_of_mass.x -= 1.0 diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out new file mode 100644 index 0000000000..c181c5dd02 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: assign() +>> runtime/assign_to_read_only_property.gd +>> 7 +>> Cannot set value into property "center_of_mass" (on base "PhysicsDirectBodyState3DExtension") because it is read-only. diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd new file mode 100644 index 0000000000..f15f580272 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.gd @@ -0,0 +1,8 @@ +func test(): + var state = PhysicsDirectBodyState3DExtension.new() + var prop = &"center_of_mass" + assign(state, prop) + state.free() + +func assign(state, prop): + state[prop].x = 1.0 diff --git a/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out new file mode 100644 index 0000000000..2cdc81aacc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/assign_to_read_only_property_with_variable_index.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: assign() +>> runtime/assign_to_read_only_property_with_variable_index.gd +>> 8 +>> Cannot set value into property "center_of_mass" (on base "PhysicsDirectBodyState3DExtension") because it is read-only. diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 6004de32f1..6e8340f618 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -22,7 +22,7 @@ </description> </method> <method name="_export_node" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="json" type="Dictionary" /> @@ -33,7 +33,7 @@ </description> </method> <method name="_export_post" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <description> Part of the export process. This method is run last, after all other parts of the export process. @@ -41,7 +41,7 @@ </description> </method> <method name="_export_preflight" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="root" type="Node" /> <description> @@ -67,7 +67,7 @@ </description> </method> <method name="_import_node" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="json" type="Dictionary" /> @@ -78,7 +78,7 @@ </description> </method> <method name="_import_post" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="root" type="Node" /> <description> @@ -87,7 +87,7 @@ </description> </method> <method name="_import_post_parse" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <description> Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_node]. @@ -95,7 +95,7 @@ </description> </method> <method name="_import_preflight" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="extensions" type="PackedStringArray" /> <description> @@ -104,7 +104,7 @@ </description> </method> <method name="_parse_node_extensions" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="extensions" type="Dictionary" /> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index b8943795a0..b322c07cec 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -264,10 +264,16 @@ </members> <constants> <constant name="HANDLE_BINARY_DISCARD_TEXTURES" value="0"> + Discards all embedded textures and uses untextured materials. </constant> <constant name="HANDLE_BINARY_EXTRACT_TEXTURES" value="1"> + Extracts embedded textures to be reimported and compressed. Editor only. Acts as uncompressed at runtime. </constant> <constant name="HANDLE_BINARY_EMBED_AS_BASISU" value="2"> + Embeds textures VRAM compressed with Basis Universal into the generated scene. + </constant> + <constant name="HANDLE_BINARY_EMBED_AS_UNCOMPRESSED" value="3"> + Embeds textures compressed losslessly into the generated scene, matching old behavior. </constant> </constants> </class> diff --git a/modules/gltf/editor/editor_import_blend_runner.cpp b/modules/gltf/editor/editor_import_blend_runner.cpp new file mode 100644 index 0000000000..c203a91834 --- /dev/null +++ b/modules/gltf/editor/editor_import_blend_runner.cpp @@ -0,0 +1,314 @@ +/**************************************************************************/ +/* editor_import_blend_runner.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_import_blend_runner.h" + +#ifdef TOOLS_ENABLED + +#include "core/io/http_client.h" +#include "editor/editor_file_system.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" + +static constexpr char PYTHON_SCRIPT_RPC[] = R"( +import bpy, sys, threading +from xmlrpc.server import SimpleXMLRPCServer +req = threading.Condition() +res = threading.Condition() +info = None +def xmlrpc_server(): + server = SimpleXMLRPCServer(('127.0.0.1', %d)) + server.register_function(export_gltf) + server.serve_forever() +def export_gltf(opts): + with req: + global info + info = ('export_gltf', opts) + req.notify() + with res: + res.wait() +if bpy.app.version < (3, 0, 0): + print('Blender 3.0 or higher is required.', file=sys.stderr) +threading.Thread(target=xmlrpc_server).start() +while True: + with req: + while info is None: + req.wait() + method, opts = info + if method == 'export_gltf': + try: + bpy.ops.wm.open_mainfile(filepath=opts['path']) + if opts['unpack_all']: + bpy.ops.file.unpack_all(method='USE_LOCAL') + bpy.ops.export_scene.gltf(**opts['gltf_options']) + except: + pass + info = None + with res: + res.notify() +)"; + +static constexpr char PYTHON_SCRIPT_DIRECT[] = R"( +import bpy, sys +opts = %s +if bpy.app.version < (3, 0, 0): + print('Blender 3.0 or higher is required.', file=sys.stderr) +bpy.ops.wm.open_mainfile(filepath=opts['path']) +if opts['unpack_all']: + bpy.ops.file.unpack_all(method='USE_LOCAL') +bpy.ops.export_scene.gltf(**opts['gltf_options']) +)"; + +String dict_to_python(const Dictionary &p_dict) { + String entries; + Array dict_keys = p_dict.keys(); + for (int i = 0; i < dict_keys.size(); i++) { + const String key = dict_keys[i]; + String value; + Variant raw_value = p_dict[key]; + + switch (raw_value.get_type()) { + case Variant::Type::BOOL: { + value = raw_value ? "True" : "False"; + break; + } + case Variant::Type::STRING: + case Variant::Type::STRING_NAME: { + value = raw_value; + value = vformat("'%s'", value.c_escape()); + break; + } + case Variant::Type::DICTIONARY: { + value = dict_to_python(raw_value); + break; + } + default: { + ERR_FAIL_V_MSG("", vformat("Unhandled Variant type %s for python dictionary", Variant::get_type_name(raw_value.get_type()))); + } + } + + entries += vformat("'%s': %s,", key, value); + } + return vformat("{%s}", entries); +} + +String dict_to_xmlrpc(const Dictionary &p_dict) { + String members; + Array dict_keys = p_dict.keys(); + for (int i = 0; i < dict_keys.size(); i++) { + const String key = dict_keys[i]; + String value; + Variant raw_value = p_dict[key]; + + switch (raw_value.get_type()) { + case Variant::Type::BOOL: { + value = vformat("<boolean>%d</boolean>", raw_value ? 1 : 0); + break; + } + case Variant::Type::STRING: + case Variant::Type::STRING_NAME: { + value = raw_value; + value = vformat("<string>%s</string>", value.xml_escape()); + break; + } + case Variant::Type::DICTIONARY: { + value = dict_to_xmlrpc(raw_value); + break; + } + default: { + ERR_FAIL_V_MSG("", vformat("Unhandled Variant type %s for XMLRPC", Variant::get_type_name(raw_value.get_type()))); + } + } + + members += vformat("<member><name>%s</name><value>%s</value></member>", key, value); + } + return vformat("<struct>%s</struct>", members); +} + +Error EditorImportBlendRunner::start_blender(const String &p_python_script, bool p_blocking) { + String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); + +#ifdef WINDOWS_ENABLED + blender_path = blender_path.path_join("blender.exe"); +#else + blender_path = blender_path.path_join("blender"); +#endif + + List<String> args; + args.push_back("--background"); + args.push_back("--python-expr"); + args.push_back(p_python_script); + + Error err; + if (p_blocking) { + int exitcode = 0; + err = OS::get_singleton()->execute(blender_path, args, nullptr, &exitcode); + if (exitcode != 0) { + return FAILED; + } + } else { + err = OS::get_singleton()->create_process(blender_path, args, &blender_pid); + } + return err; +} + +Error EditorImportBlendRunner::do_import(const Dictionary &p_options) { + if (is_using_rpc()) { + return do_import_rpc(p_options); + } else { + return do_import_direct(p_options); + } +} + +Error EditorImportBlendRunner::do_import_rpc(const Dictionary &p_options) { + kill_timer->stop(); + + // Start Blender if not already running. + if (!is_running()) { + // Start an XML RPC server on the given port. + String python = vformat(PYTHON_SCRIPT_RPC, rpc_port); + Error err = start_blender(python, false); + if (err != OK || blender_pid == 0) { + return FAILED; + } + } + + // Convert options to XML body. + String xml_options = dict_to_xmlrpc(p_options); + String xml_body = vformat("<?xml version=\"1.0\"?><methodCall><methodName>export_gltf</methodName><params><param><value>%s</value></param></params></methodCall>", xml_options); + + // Connect to RPC server. + Ref<HTTPClient> client = HTTPClient::create(); + client->connect_to_host("127.0.0.1", rpc_port); + + bool done = false; + while (!done) { + HTTPClient::Status status = client->get_status(); + switch (status) { + case HTTPClient::STATUS_RESOLVING: + case HTTPClient::STATUS_CONNECTING: { + client->poll(); + break; + } + case HTTPClient::STATUS_CONNECTED: { + done = true; + break; + } + default: { + ERR_FAIL_V_MSG(ERR_CONNECTION_ERROR, vformat("Unexpected status during RPC connection: %d", status)); + } + } + } + + // Send XML request. + PackedByteArray xml_buffer = xml_body.to_utf8_buffer(); + Error err = client->request(HTTPClient::METHOD_POST, "/", Vector<String>(), xml_buffer.ptr(), xml_buffer.size()); + if (err != OK) { + ERR_FAIL_V_MSG(err, vformat("Unable to send RPC request: %d", err)); + } + + // Wait for response. + done = false; + while (!done) { + HTTPClient::Status status = client->get_status(); + switch (status) { + case HTTPClient::STATUS_REQUESTING: { + client->poll(); + break; + } + case HTTPClient::STATUS_BODY: { + client->poll(); + // Parse response here if needed. For now we can just ignore it. + done = true; + break; + } + default: { + ERR_FAIL_V_MSG(ERR_CONNECTION_ERROR, vformat("Unexpected status during RPC response: %d", status)); + } + } + } + + return OK; +} + +Error EditorImportBlendRunner::do_import_direct(const Dictionary &p_options) { + // Export glTF directly. + String python = vformat(PYTHON_SCRIPT_DIRECT, dict_to_python(p_options)); + Error err = start_blender(python, true); + if (err != OK) { + return err; + } + + return OK; +} + +void EditorImportBlendRunner::_resources_reimported(const PackedStringArray &p_files) { + if (is_running()) { + // After a batch of imports is done, wait a few seconds before trying to kill blender, + // in case of having multiple imports trigger in quick succession. + kill_timer->start(); + } +} + +void EditorImportBlendRunner::_kill_blender() { + kill_timer->stop(); + if (is_running()) { + OS::get_singleton()->kill(blender_pid); + } + blender_pid = 0; +} + +void EditorImportBlendRunner::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_PREDELETE: { + _kill_blender(); + break; + } + } +} + +EditorImportBlendRunner *EditorImportBlendRunner::singleton = nullptr; + +EditorImportBlendRunner::EditorImportBlendRunner() { + ERR_FAIL_COND_MSG(singleton != nullptr, "EditorImportBlendRunner already created."); + singleton = this; + + rpc_port = EDITOR_GET("filesystem/import/blender/rpc_port"); + + kill_timer = memnew(Timer); + add_child(kill_timer); + kill_timer->set_one_shot(true); + kill_timer->set_wait_time(EDITOR_GET("filesystem/import/blender/rpc_server_uptime")); + kill_timer->connect("timeout", callable_mp(this, &EditorImportBlendRunner::_kill_blender)); + + EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &EditorImportBlendRunner::_resources_reimported)); +} + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_import_blend_runner.h b/modules/gltf/editor/editor_import_blend_runner.h new file mode 100644 index 0000000000..b2b82394e1 --- /dev/null +++ b/modules/gltf/editor/editor_import_blend_runner.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/* editor_import_blend_runner.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef EDITOR_IMPORT_BLEND_RUNNER_H +#define EDITOR_IMPORT_BLEND_RUNNER_H + +#ifdef TOOLS_ENABLED + +#include "core/os/os.h" +#include "scene/main/node.h" +#include "scene/main/timer.h" + +class EditorImportBlendRunner : public Node { + GDCLASS(EditorImportBlendRunner, Node); + + static EditorImportBlendRunner *singleton; + + Timer *kill_timer; + void _resources_reimported(const PackedStringArray &p_files); + void _kill_blender(); + void _notification(int p_what); + +protected: + int rpc_port = 0; + OS::ProcessID blender_pid = 0; + Error start_blender(const String &p_python_script, bool p_blocking); + Error do_import_direct(const Dictionary &p_options); + Error do_import_rpc(const Dictionary &p_options); + +public: + static EditorImportBlendRunner *get_singleton() { return singleton; } + + bool is_running() { return blender_pid != 0 && OS::get_singleton()->is_process_running(blender_pid); } + bool is_using_rpc() { return rpc_port != 0; } + Error do_import(const Dictionary &p_options); + + EditorImportBlendRunner(); +}; + +#endif // TOOLS_ENABLED + +#endif // EDITOR_IMPORT_BLEND_RUNNER_H diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 5415c5818f..520f33261a 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -34,6 +34,7 @@ #include "../gltf_defines.h" #include "../gltf_document.h" +#include "editor_import_blend_runner.h" #include "core/config/project_settings.h" #include "editor/editor_file_dialog.h" @@ -68,149 +69,129 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ // Handle configuration options. - String parameters_arg; + Dictionary request_options; + Dictionary parameters_map; + + parameters_map["filepath"] = sink_global; + parameters_map["export_keep_originals"] = true; + parameters_map["export_format"] = "GLTF_SEPARATE"; + parameters_map["export_yup"] = true; if (p_options.has(SNAME("blender/nodes/custom_properties")) && p_options[SNAME("blender/nodes/custom_properties")]) { - parameters_arg += "export_extras=True,"; + parameters_map["export_extras"] = true; } else { - parameters_arg += "export_extras=False,"; + parameters_map["export_extras"] = false; } if (p_options.has(SNAME("blender/meshes/skins"))) { int32_t skins = p_options["blender/meshes/skins"]; if (skins == BLEND_BONE_INFLUENCES_NONE) { - parameters_arg += "export_skins=False,"; + parameters_map["export_skins"] = false; } else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) { - parameters_arg += "export_all_influences=False,export_skins=True,"; + parameters_map["export_skins"] = true; + parameters_map["export_all_influences"] = false; } else if (skins == BLEND_BONE_INFLUENCES_ALL) { - parameters_arg += "export_all_influences=True,export_skins=True,"; + parameters_map["export_skins"] = true; + parameters_map["export_all_influences"] = true; } } else { - parameters_arg += "export_skins=False,"; + parameters_map["export_skins"] = false; } if (p_options.has(SNAME("blender/materials/export_materials"))) { int32_t exports = p_options["blender/materials/export_materials"]; if (exports == BLEND_MATERIAL_EXPORT_PLACEHOLDER) { - parameters_arg += "export_materials='PLACEHOLDER',"; + parameters_map["export_materials"] = "PLACEHOLDER"; } else if (exports == BLEND_MATERIAL_EXPORT_EXPORT) { - parameters_arg += "export_materials='EXPORT',"; + parameters_map["export_materials"] = "EXPORT"; } } else { - parameters_arg += "export_materials='PLACEHOLDER',"; + parameters_map["export_materials"] = "PLACEHOLDER"; } if (p_options.has(SNAME("blender/nodes/cameras")) && p_options[SNAME("blender/nodes/cameras")]) { - parameters_arg += "export_cameras=True,"; + parameters_map["export_cameras"] = true; } else { - parameters_arg += "export_cameras=False,"; + parameters_map["export_cameras"] = false; } if (p_options.has(SNAME("blender/nodes/punctual_lights")) && p_options[SNAME("blender/nodes/punctual_lights")]) { - parameters_arg += "export_lights=True,"; + parameters_map["export_lights"] = true; } else { - parameters_arg += "export_lights=False,"; + parameters_map["export_lights"] = false; } if (p_options.has(SNAME("blender/meshes/colors")) && p_options[SNAME("blender/meshes/colors")]) { - parameters_arg += "export_colors=True,"; + parameters_map["export_colors"] = true; } else { - parameters_arg += "export_colors=False,"; + parameters_map["export_colors"] = false; } if (p_options.has(SNAME("blender/nodes/visible"))) { int32_t visible = p_options["blender/nodes/visible"]; if (visible == BLEND_VISIBLE_VISIBLE_ONLY) { - parameters_arg += "use_visible=True,"; + parameters_map["use_visible"] = true; } else if (visible == BLEND_VISIBLE_RENDERABLE) { - parameters_arg += "use_renderable=True,"; + parameters_map["use_renderable"] = true; } else if (visible == BLEND_VISIBLE_ALL) { - parameters_arg += "use_visible=False,use_renderable=False,"; + parameters_map["use_renderable"] = false; + parameters_map["use_visible"] = false; } } else { - parameters_arg += "use_visible=False,use_renderable=False,"; + parameters_map["use_renderable"] = false; + parameters_map["use_visible"] = false; } if (p_options.has(SNAME("blender/meshes/uvs")) && p_options[SNAME("blender/meshes/uvs")]) { - parameters_arg += "export_texcoords=True,"; + parameters_map["export_texcoords"] = true; } else { - parameters_arg += "export_texcoords=False,"; + parameters_map["export_texcoords"] = false; } if (p_options.has(SNAME("blender/meshes/normals")) && p_options[SNAME("blender/meshes/normals")]) { - parameters_arg += "export_normals=True,"; + parameters_map["export_normals"] = true; } else { - parameters_arg += "export_normals=False,"; + parameters_map["export_normals"] = false; } if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) { - parameters_arg += "export_tangents=True,"; + parameters_map["export_tangents"] = true; } else { - parameters_arg += "export_tangents=False,"; + parameters_map["export_tangents"] = false; } if (p_options.has(SNAME("blender/animation/group_tracks")) && p_options[SNAME("blender/animation/group_tracks")]) { - parameters_arg += "export_nla_strips=True,"; + parameters_map["export_nla_strips"] = true; } else { - parameters_arg += "export_nla_strips=False,"; + parameters_map["export_nla_strips"] = false; } if (p_options.has(SNAME("blender/animation/limit_playback")) && p_options[SNAME("blender/animation/limit_playback")]) { - parameters_arg += "export_frame_range=True,"; + parameters_map["export_frame_range"] = true; } else { - parameters_arg += "export_frame_range=False,"; + parameters_map["export_frame_range"] = false; } if (p_options.has(SNAME("blender/animation/always_sample")) && p_options[SNAME("blender/animation/always_sample")]) { - parameters_arg += "export_force_sampling=True,"; + parameters_map["export_force_sampling"] = true; } else { - parameters_arg += "export_force_sampling=False,"; + parameters_map["export_force_sampling"] = false; } if (p_options.has(SNAME("blender/meshes/export_bones_deforming_mesh_only")) && p_options[SNAME("blender/meshes/export_bones_deforming_mesh_only")]) { - parameters_arg += "export_def_bones=True,"; + parameters_map["export_def_bones"] = true; } else { - parameters_arg += "export_def_bones=False,"; + parameters_map["export_def_bones"] = false; } if (p_options.has(SNAME("blender/nodes/modifiers")) && p_options[SNAME("blender/nodes/modifiers")]) { - parameters_arg += "export_apply=True"; + parameters_map["export_apply"] = true; } else { - parameters_arg += "export_apply=False"; + parameters_map["export_apply"] = false; } - String unpack_all; if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { - unpack_all = "bpy.ops.file.unpack_all(method='USE_LOCAL');"; + request_options["unpack_all"] = true; + } else { + request_options["unpack_all"] = false; } - // Prepare Blender export script. - - String common_args = vformat("filepath='%s',", sink_global) + - "export_format='GLTF_SEPARATE'," - "export_yup=True," + - parameters_arg; - String export_script = - String("import bpy, sys;") + - "print('Blender 3.0 or higher is required.', file=sys.stderr) if bpy.app.version < (3, 0, 0) else None;" + - vformat("bpy.ops.wm.open_mainfile(filepath='%s');", source_global) + - unpack_all + - vformat("bpy.ops.export_scene.gltf(export_keep_originals=True,%s);", common_args); - print_verbose(export_script); - - // Run script with configured Blender binary. + request_options["path"] = source_global; + request_options["gltf_options"] = parameters_map; - String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); - -#ifdef WINDOWS_ENABLED - blender_path = blender_path.path_join("blender.exe"); -#else - blender_path = blender_path.path_join("blender"); -#endif - - List<String> args; - args.push_back("--background"); - args.push_back("--python-expr"); - args.push_back(export_script); - - String standard_out; - int ret; - OS::get_singleton()->execute(blender_path, args, &standard_out, &ret, true); - print_verbose(blender_path); - print_verbose(standard_out); - - if (ret != 0) { + // Run Blender and export glTF. + Error err = EditorImportBlendRunner::get_singleton()->do_import(request_options); + if (err != OK) { if (r_err) { *r_err = ERR_SCRIPT_FAILED; } - ERR_PRINT(vformat("Blend export to glTF failed with error: %d.", ret)); return nullptr; } @@ -226,7 +207,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { base_dir = sink.get_base_dir(); } - Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir); + err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir); if (err != OK) { if (r_err) { *r_err = FAILED; diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index 67bbf8dd15..012a144d52 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -51,8 +51,8 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t doc.instantiate(); Ref<GLTFState> state; state.instantiate(); - if (p_options.has("meshes/handle_gltf_embedded_images")) { - int32_t enum_option = p_options["meshes/handle_gltf_embedded_images"]; + if (p_options.has("gltf/embedded_image_handling")) { + int32_t enum_option = p_options["gltf/embedded_image_handling"]; state->set_handle_binary_image(enum_option); } Error err = doc->append_from_file(p_path, state, p_flags); @@ -87,7 +87,7 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) { - r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "meshes/handle_gltf_embedded_images", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES)); + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES)); } #endif // TOOLS_ENABLED diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 496a8f6cb8..bedb42eb32 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -49,9 +49,9 @@ void GLTFDocumentExtension::_bind_methods() { // Import process. Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err); - return Error(err); + return err; } Vector<String> GLTFDocumentExtension::get_supported_extensions() { @@ -63,9 +63,9 @@ Vector<String> GLTFDocumentExtension::get_supported_extensions() { Error GLTFDocumentExtension::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_parse_node_extensions, p_state, p_gltf_node, p_extensions, err); - return Error(err); + return err; } Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { @@ -79,34 +79,34 @@ Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<G Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_post_parse, p_state, err); - return Error(err); + return err; } Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err); - return Error(err); + return err; } Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_post, p_state, p_root, err); - return Error(err); + return err; } // Export process. Error GLTFDocumentExtension::export_preflight(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_preflight, p_state, p_root, err); - return Error(err); + return err; } void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) { @@ -120,14 +120,14 @@ Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err); - return Error(err); + return err; } Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_post, p_state, err); - return Error(err); + return err; } diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index 97f5045a1c..3531f81f6f 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -55,18 +55,18 @@ public: virtual Error export_post(Ref<GLTFState> p_state); // Import process. - GDVIRTUAL2R(int, _import_preflight, Ref<GLTFState>, Vector<String>); + GDVIRTUAL2R(Error, _import_preflight, Ref<GLTFState>, Vector<String>); GDVIRTUAL0R(Vector<String>, _get_supported_extensions); - GDVIRTUAL3R(int, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); + GDVIRTUAL3R(Error, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); - GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>); - GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); - GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *); + GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>); + GDVIRTUAL4R(Error, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL2R(Error, _import_post, Ref<GLTFState>, Node *); // Export process. - GDVIRTUAL2R(int, _export_preflight, Ref<GLTFState>, Node *); + GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *); GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); - GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); - GDVIRTUAL1R(int, _export_post, Ref<GLTFState>); + GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>); }; #endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 5950ad33b5..1a09b5bdcc 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -32,6 +32,7 @@ #include "extensions/gltf_spec_gloss.h" +#include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" @@ -3220,8 +3221,8 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); - continue; - } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { +#ifdef TOOLS_ENABLED + } else if (Engine::get_singleton()->is_editor_hint() && GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { if (p_state->base_path.is_empty()) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); @@ -3230,26 +3231,56 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); } else { + Error err = OK; + bool must_import = false; String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png"; - Ref<ConfigFile> config; - config.instantiate(); - if (FileAccess::exists(file_path + ".import")) { - config->load(file_path + ".import"); + if (!FileAccess::exists(file_path + ".import")) { + Ref<ConfigFile> config; + config.instantiate(); + config->set_value("remap", "importer", "texture"); + config->set_value("remap", "type", "Texture2D"); + // Currently, it will likely use project defaults of Detect 3D, so textures will be reimported again. + if (!config->has_section_key("params", "mipmaps/generate")) { + config->set_value("params", "mipmaps/generate", true); + } + + if (ProjectSettings::get_singleton()->has_setting("importer_defaults/texture")) { + //use defaults if exist + Dictionary importer_defaults = GLOBAL_GET("importer_defaults/texture"); + List<Variant> importer_def_keys; + importer_defaults.get_key_list(&importer_def_keys); + for (const Variant &key : importer_def_keys) { + if (!config->has_section_key("params", (String)key)) { + config->set_value("params", (String)key, importer_defaults[key]); + } + } + } + err = config->save(file_path + ".import"); + ERR_FAIL_COND_V(err != OK, err); + must_import = true; } - config->set_value("remap", "importer", "texture"); - config->set_value("remap", "type", "Texture2D"); - if (!config->has_section_key("params", "compress/mode")) { - config->set_value("remap", "compress/mode", 2); //user may want another compression, so leave it bes + Vector<uint8_t> png_buffer = img->save_png_to_buffer(); + if (ResourceLoader::exists(file_path)) { + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::READ, &err); + if (err == OK && file.is_valid()) { + Vector<uint8_t> orig_png_buffer = file->get_buffer(file->get_length()); + if (png_buffer != orig_png_buffer) { + must_import = true; + } + } + } else { + must_import = true; } - if (!config->has_section_key("params", "mipmaps/generate")) { - config->set_value("params", "mipmaps/generate", true); + if (must_import) { + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err != OK, err); + ERR_FAIL_COND_V(file.is_null(), FAILED); + file->store_buffer(png_buffer); + file->flush(); + file.unref(); + // ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed. + ResourceLoader::import(file_path); } - Error err = OK; - err = config->save(file_path + ".import"); - ERR_FAIL_COND_V(err != OK, err); - img->save_png(file_path); - ERR_FAIL_COND_V(err != OK, err); - ResourceLoader::import(file_path); Ref<Texture2D> saved_image = ResourceLoader::load(file_path, "Texture2D"); if (saved_image.is_valid()) { p_state->images.push_back(saved_image); @@ -3261,7 +3292,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p p_state->source_images.push_back(Ref<Image>()); } } - continue; +#endif } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { Ref<PortableCompressedTexture2D> tex; tex.instantiate(); @@ -3271,11 +3302,15 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL); p_state->images.push_back(tex); p_state->source_images.push_back(img); - continue; + } else { + // This handles two cases: if editor hint and HANDLE_BINARY_EXTRACT_TEXTURES; or if HANDLE_BINARY_EMBED_AS_UNCOMPRESSED + Ref<ImageTexture> tex; + tex.instantiate(); + tex->set_name(img->get_name()); + tex->set_image(img); + p_state->images.push_back(tex); + p_state->source_images.push_back(img); } - - p_state->images.push_back(Ref<Texture2D>()); - p_state->source_images.push_back(Ref<Image>()); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index b67484fc8e..b7b7113a97 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -120,11 +120,12 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex, ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> - ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum + ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES); BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES); BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_BASISU); + BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_UNCOMPRESSED); } void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) { diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 52d7949d03..b6979ca48e 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -108,6 +108,7 @@ public: HANDLE_BINARY_DISCARD_TEXTURES = 0, HANDLE_BINARY_EXTRACT_TEXTURES, HANDLE_BINARY_EMBED_AS_BASISU, + HANDLE_BINARY_EMBED_AS_UNCOMPRESSED, // if this value changes from 3, ResourceImporterScene::pre_import must be changed as well. }; int32_t get_handle_binary_image() { return handle_binary_image; diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index f80e12bbae..78589090db 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -36,6 +36,7 @@ #ifdef TOOLS_ENABLED #include "core/config/project_settings.h" +#include "editor/editor_import_blend_runner.h" #include "editor/editor_node.h" #include "editor/editor_scene_exporter_gltf_plugin.h" #include "editor/editor_scene_importer_blend.h" @@ -52,6 +53,14 @@ static void _editor_init() { bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. + EDITOR_DEF_RST("filesystem/import/blender/rpc_port", 6011); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, + "filesystem/import/blender/rpc_port", PROPERTY_HINT_RANGE, "0,65535,1")); + + EDITOR_DEF_RST("filesystem/import/blender/rpc_server_uptime", 5); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, + "filesystem/import/blender/rpc_server_uptime", PROPERTY_HINT_RANGE, "0,300,1,or_greater,suffix:s")); + String blender3_path = EDITOR_DEF_RST("filesystem/import/blender/blender3_path", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR)); @@ -71,6 +80,8 @@ static void _editor_init() { EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query); } } + memnew(EditorImportBlendRunner); + EditorNode::get_singleton()->add_child(EditorImportBlendRunner::get_singleton()); // FBX to glTF importer. diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs index 813bdf1e9f..47a4516948 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs @@ -48,7 +48,7 @@ namespace GodotPlugins.Game } catch (Exception e) { - Console.Error.WriteLine(e); + global::System.Console.Error.WriteLine(e); return false.ToGodotBool(); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 019504ad66..70b48b0e3a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -185,7 +185,9 @@ namespace GodotTools.Export foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories)) { - AddSharedObject(file, tags: null, projectDataDirName); + AddSharedObject(file, tags: null, + Path.Join(projectDataDirName, + Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file)))); } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs index 8463403096..4610761bdb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs @@ -7,7 +7,7 @@ namespace Godot /// <summary> /// Instantiates the scene's node hierarchy, erroring on failure. /// Triggers child scene instantiation(s). Triggers a - /// <see cref="Node.NotificationInstanced"/> notification on the root node. + /// <see cref="Node.NotificationSceneInstantiated"/> notification on the root node. /// </summary> /// <seealso cref="InstantiateOrNull{T}(GenEditState)"/> /// <exception cref="InvalidCastException"> @@ -23,7 +23,7 @@ namespace Godot /// <summary> /// Instantiates the scene's node hierarchy, returning <see langword="null"/> on failure. /// Triggers child scene instantiation(s). Triggers a - /// <see cref="Node.NotificationInstanced"/> notification on the root node. + /// <see cref="Node.NotificationSceneInstantiated"/> notification on the root node. /// </summary> /// <seealso cref="Instantiate{T}(GenEditState)"/> /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 1e23689c95..8ac18c4314 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -459,6 +459,10 @@ namespace Godot.NativeInterop public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self); + public static partial godot_bool godotsharp_node_path_equals(in godot_node_path p_self, in godot_node_path p_other); + + public static partial int godotsharp_node_path_hash(in godot_node_path p_self); + // GD, etc internal static partial void godotsharp_bytes_to_var(in godot_packed_byte_array p_bytes, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index b02bd167a1..f216fb7ea3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -39,7 +39,7 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// </code> /// </example> - public sealed class NodePath : IDisposable + public sealed class NodePath : IDisposable, IEquatable<NodePath> { internal godot_node_path.movable NativeValue; @@ -288,5 +288,37 @@ namespace Godot /// </summary> /// <returns>If the <see cref="NodePath"/> is empty.</returns> public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; + + public static bool operator ==(NodePath left, NodePath right) + { + if (left is null) + return right is null; + return left.Equals(right); + } + + public static bool operator !=(NodePath left, NodePath right) + { + return !(left == right); + } + + public bool Equals(NodePath other) + { + if (other is null) + return false; + var self = (godot_node_path)NativeValue; + var otherNative = (godot_node_path)other.NativeValue; + return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool(); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other)); + } + + public override int GetHashCode() + { + var self = (godot_node_path)NativeValue; + return NativeFuncs.godotsharp_node_path_hash(self); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index b9ee0bc278..97d28f9ee9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -10,7 +10,7 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// </summary> - public sealed class StringName : IDisposable + public sealed class StringName : IDisposable, IEquatable<StringName> { internal godot_string_name.movable NativeValue; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 5283dc7ec6..d7392dbda8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -247,19 +247,19 @@ namespace Godot /// <returns>The orthonormalized transform.</returns> public readonly Transform2D Orthonormalized() { - Transform2D on = this; + Transform2D ortho = this; - Vector2 onX = on.X; - Vector2 onY = on.Y; + Vector2 orthoX = ortho.X; + Vector2 orthoY = ortho.Y; - onX.Normalize(); - onY = onY - (onX * onX.Dot(onY)); - onY.Normalize(); + orthoX.Normalize(); + orthoY = orthoY - orthoX * orthoX.Dot(orthoY); + orthoY.Normalize(); - on.X = onX; - on.Y = onY; + ortho.X = orthoX; + ortho.Y = orthoY; - return on; + return ortho; } /// <summary> diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index d17fe3e75f..a90b07620c 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -1141,6 +1141,14 @@ bool godotsharp_node_path_is_absolute(const NodePath *p_self) { return p_self->is_absolute(); } +bool godotsharp_node_path_equals(const NodePath *p_self, const NodePath *p_other) { + return *p_self == *p_other; +} + +int godotsharp_node_path_hash(const NodePath *p_self) { + return p_self->hash(); +} + void godotsharp_randomize() { Math::randomize(); } @@ -1477,6 +1485,8 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_node_path_get_subname, (void *)godotsharp_node_path_get_subname_count, (void *)godotsharp_node_path_is_absolute, + (void *)godotsharp_node_path_equals, + (void *)godotsharp_node_path_hash, (void *)godotsharp_bytes_to_var, (void *)godotsharp_convert, (void *)godotsharp_hash, diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index d546c5d3ba..c3cb1c5f13 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -262,7 +262,7 @@ TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const { const NavMap *map = map_owner.get_or_null(p_map); ERR_FAIL_COND_V(map == nullptr, agents_rids); - const LocalVector<RvoAgent *> agents = map->get_agents(); + const LocalVector<NavAgent *> agents = map->get_agents(); agents_rids.resize(agents.size()); for (uint32_t i = 0; i < agents.size(); i++) { @@ -282,7 +282,7 @@ RID GodotNavigationServer::region_get_map(RID p_region) const { } RID GodotNavigationServer::agent_get_map(RID p_agent) const { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND_V(agent == nullptr, RID()); if (agent->get_map()) { @@ -579,13 +579,13 @@ RID GodotNavigationServer::agent_create() { MutexLock lock(operations_mutex); RID rid = agent_owner.make_rid(); - RvoAgent *agent = agent_owner.get_or_null(rid); + NavAgent *agent = agent_owner.get_or_null(rid); agent->set_self(rid); return rid; } COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); if (agent->get_map()) { @@ -612,77 +612,77 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) { } COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->neighborDist_ = p_distance; } COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->maxNeighbors_ = p_count; } COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->timeHorizon_ = p_time; } COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->radius_ = p_radius; } COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->maxSpeed_ = p_max_speed; } COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z); } COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z); } COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z); } COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->get_agent()->ignore_y_ = p_ignore; } bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND_V(agent == nullptr, false); return agent->is_map_changed(); } COMMAND_2(agent_set_callback, RID, p_agent, Callable, p_callback) { - RvoAgent *agent = agent_owner.get_or_null(p_agent); + NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); agent->set_callback(p_callback); @@ -713,7 +713,7 @@ COMMAND_1(free, RID, p_object) { } // Remove any assigned agent - for (RvoAgent *agent : map->get_agents()) { + for (NavAgent *agent : map->get_agents()) { map->remove_agent(agent); agent->set_map(nullptr); } @@ -746,7 +746,7 @@ COMMAND_1(free, RID, p_object) { link_owner.free(p_object); } else if (agent_owner.owns(p_object)) { - RvoAgent *agent = agent_owner.get_or_null(p_object); + NavAgent *agent = agent_owner.get_or_null(p_object); // Removes this agent from the map if assigned if (agent->get_map() != nullptr) { diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index eea5713c40..0b113b77d4 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -36,10 +36,10 @@ #include "core/templates/rid_owner.h" #include "servers/navigation_server_3d.h" +#include "nav_agent.h" #include "nav_link.h" #include "nav_map.h" #include "nav_region.h" -#include "rvo_agent.h" /// The commands are functions executed during the `sync` phase. @@ -71,7 +71,7 @@ class GodotNavigationServer : public NavigationServer3D { mutable RID_Owner<NavLink> link_owner; mutable RID_Owner<NavMap> map_owner; mutable RID_Owner<NavRegion> region_owner; - mutable RID_Owner<RvoAgent> agent_owner; + mutable RID_Owner<NavAgent> agent_owner; bool active = true; LocalVector<NavMap *> active_maps; diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/nav_agent.cpp index 40f1e925be..293544c0a5 100644 --- a/modules/navigation/rvo_agent.cpp +++ b/modules/navigation/nav_agent.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* rvo_agent.cpp */ +/* nav_agent.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "rvo_agent.h" +#include "nav_agent.h" #include "nav_map.h" -void RvoAgent::set_map(NavMap *p_map) { +void NavAgent::set_map(NavMap *p_map) { map = p_map; } -bool RvoAgent::is_map_changed() { +bool NavAgent::is_map_changed() { if (map) { bool is_changed = map->get_map_update_id() != map_update_id; map_update_id = map->get_map_update_id(); @@ -46,15 +46,15 @@ bool RvoAgent::is_map_changed() { } } -void RvoAgent::set_callback(Callable p_callback) { +void NavAgent::set_callback(Callable p_callback) { callback = p_callback; } -bool RvoAgent::has_callback() const { +bool NavAgent::has_callback() const { return callback.is_valid(); } -void RvoAgent::dispatch_callback() { +void NavAgent::dispatch_callback() { if (!callback.is_valid()) { return; } diff --git a/modules/navigation/rvo_agent.h b/modules/navigation/nav_agent.h index 5f377b6079..f154ce14d9 100644 --- a/modules/navigation/rvo_agent.h +++ b/modules/navigation/nav_agent.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* rvo_agent.h */ +/* nav_agent.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef RVO_AGENT_H -#define RVO_AGENT_H +#ifndef NAV_AGENT_H +#define NAV_AGENT_H #include "core/object/class_db.h" #include "nav_rid.h" @@ -38,7 +38,7 @@ class NavMap; -class RvoAgent : public NavRid { +class NavAgent : public NavRid { NavMap *map = nullptr; RVO::Agent agent; Callable callback = Callable(); @@ -62,4 +62,4 @@ public: void dispatch_callback(); }; -#endif // RVO_AGENT_H +#endif // NAV_AGENT_H diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index d763b1d3bc..b1674c8fc5 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -31,9 +31,9 @@ #include "nav_map.h" #include "core/object/worker_thread_pool.h" +#include "nav_agent.h" #include "nav_link.h" #include "nav_region.h" -#include "rvo_agent.h" #include <algorithm> #define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a))) @@ -568,18 +568,18 @@ void NavMap::remove_link(NavLink *p_link) { } } -bool NavMap::has_agent(RvoAgent *agent) const { +bool NavMap::has_agent(NavAgent *agent) const { return (agents.find(agent) != -1); } -void NavMap::add_agent(RvoAgent *agent) { +void NavMap::add_agent(NavAgent *agent) { if (!has_agent(agent)) { agents.push_back(agent); agents_dirty = true; } } -void NavMap::remove_agent(RvoAgent *agent) { +void NavMap::remove_agent(NavAgent *agent) { remove_agent_as_controlled(agent); int64_t agent_index = agents.find(agent); if (agent_index != -1) { @@ -588,7 +588,7 @@ void NavMap::remove_agent(RvoAgent *agent) { } } -void NavMap::set_agent_as_controlled(RvoAgent *agent) { +void NavMap::set_agent_as_controlled(NavAgent *agent) { const bool exist = (controlled_agents.find(agent) != -1); if (!exist) { ERR_FAIL_COND(!has_agent(agent)); @@ -596,7 +596,7 @@ void NavMap::set_agent_as_controlled(RvoAgent *agent) { } } -void NavMap::remove_agent_as_controlled(RvoAgent *agent) { +void NavMap::remove_agent_as_controlled(NavAgent *agent) { int64_t active_avoidance_agent_index = controlled_agents.find(agent); if (active_avoidance_agent_index != -1) { controlled_agents.remove_at_unordered(active_avoidance_agent_index); @@ -895,7 +895,7 @@ void NavMap::sync() { // cannot use LocalVector here as RVO library expects std::vector to build KdTree std::vector<RVO::Agent *> raw_agents; raw_agents.reserve(agents.size()); - for (RvoAgent *agent : agents) { + for (NavAgent *agent : agents) { raw_agents.push_back(agent->get_agent()); } rvo.buildAgentTree(raw_agents); @@ -916,7 +916,7 @@ void NavMap::sync() { pm_edge_free_count = _new_pm_edge_free_count; } -void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) { +void NavMap::compute_single_step(uint32_t index, NavAgent **agent) { (*(agent + index))->get_agent()->computeNeighbors(&rvo); (*(agent + index))->get_agent()->computeNewVelocity(deltatime); } @@ -930,7 +930,7 @@ void NavMap::step(real_t p_deltatime) { } void NavMap::dispatch_callbacks() { - for (RvoAgent *agent : controlled_agents) { + for (NavAgent *agent : controlled_agents) { agent->dispatch_callback(); } } diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index fce7aff3ba..ab6a48dd70 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -42,7 +42,7 @@ class NavLink; class NavRegion; -class RvoAgent; +class NavAgent; class NavMap : public NavRid { /// Map Up @@ -78,10 +78,10 @@ class NavMap : public NavRid { bool agents_dirty = false; /// All the Agents (even the controlled one) - LocalVector<RvoAgent *> agents; + LocalVector<NavAgent *> agents; /// Controlled agents - LocalVector<RvoAgent *> controlled_agents; + LocalVector<NavAgent *> controlled_agents; /// Physics delta time real_t deltatime = 0.0; @@ -144,15 +144,15 @@ public: return links; } - bool has_agent(RvoAgent *agent) const; - void add_agent(RvoAgent *agent); - void remove_agent(RvoAgent *agent); - const LocalVector<RvoAgent *> &get_agents() const { + bool has_agent(NavAgent *agent) const; + void add_agent(NavAgent *agent); + void remove_agent(NavAgent *agent); + const LocalVector<NavAgent *> &get_agents() const { return agents; } - void set_agent_as_controlled(RvoAgent *agent); - void remove_agent_as_controlled(RvoAgent *agent); + void set_agent_as_controlled(NavAgent *agent); + void remove_agent_as_controlled(NavAgent *agent); uint32_t get_map_update_id() const { return map_update_id; @@ -173,7 +173,7 @@ public: int get_pm_edge_free_count() const { return pm_edge_free_count; } private: - void compute_single_step(uint32_t index, RvoAgent **agent); + void compute_single_step(uint32_t index, NavAgent **agent); void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const; }; diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 3b39967ba4..0dd41675b6 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -103,6 +103,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extens env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_ml2_controller_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/extensions/openxr_ml2_controller_extension.cpp b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp new file mode 100644 index 0000000000..ae372f69b3 --- /dev/null +++ b/modules/openxr/extensions/openxr_ml2_controller_extension.cpp @@ -0,0 +1,71 @@ +/**************************************************************************/ +/* openxr_ml2_controller_extension.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "openxr_ml2_controller_extension.h" +#include "../action_map/openxr_interaction_profile_meta_data.h" + +HashMap<String, bool *> OpenXRML2ControllerExtension::get_requested_extensions() { + HashMap<String, bool *> request_extensions; + + request_extensions[XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available; + + return request_extensions; +} + +bool OpenXRML2ControllerExtension::is_available() { + return available; +} + +void OpenXRML2ControllerExtension::on_register_metadata() { + OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton(); + ERR_FAIL_NULL(metadata); + + // Magic Leap 2 Controller + const String profile_path = "/interaction_profiles/ml/ml2_controller"; + metadata->register_interaction_profile("Magic Leap 2 controller", "/interaction_profiles/ml/ml2_controller", XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME); + for (const String user_path : { "/user/hand/left", "/user/hand/right" }) { + metadata->register_io_path(profile_path, "Grip pose", user_path, user_path + "/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + metadata->register_io_path(profile_path, "Aim pose", user_path, user_path + "/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE); + + metadata->register_io_path(profile_path, "Menu click", user_path, user_path + "/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path(profile_path, "Trigger", user_path, user_path + "/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trigger click", user_path, user_path + "/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path(profile_path, "Shoulder click", user_path, user_path + "/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + + metadata->register_io_path(profile_path, "Trackpad click", user_path, user_path + "/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL); + metadata->register_io_path(profile_path, "Trackpad force", user_path, user_path + "/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad X", user_path, user_path + "/input/trackpad/x", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad Y", user_path, user_path + "/input/trackpad/y", "", OpenXRAction::OPENXR_ACTION_FLOAT); + metadata->register_io_path(profile_path, "Trackpad touch", user_path, user_path + "/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_VECTOR2); + + metadata->register_io_path(profile_path, "Haptic output", user_path, user_path + "/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC); + } +} diff --git a/modules/openxr/extensions/openxr_ml2_controller_extension.h b/modules/openxr/extensions/openxr_ml2_controller_extension.h new file mode 100644 index 0000000000..216cd55a2f --- /dev/null +++ b/modules/openxr/extensions/openxr_ml2_controller_extension.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* openxr_ml2_controller_extension.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OPENXR_ML2_CONTROLLER_EXTENSION_H +#define OPENXR_ML2_CONTROLLER_EXTENSION_H + +#include "openxr_extension_wrapper.h" + +class OpenXRML2ControllerExtension : public OpenXRExtensionWrapper { +public: + virtual HashMap<String, bool *> get_requested_extensions() override; + + bool is_available(); + + virtual void on_register_metadata() override; + +private: + bool available = false; +}; + +#endif // OPENXR_ML2_CONTROLLER_EXTENSION_H diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 0a25cd68b7..ddb3114b59 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -483,6 +483,37 @@ bool OpenXRAPI::load_supported_view_configuration_types() { return true; } +bool OpenXRAPI::load_supported_environmental_blend_modes() { + // This queries the supported environmental blend modes. + + ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false); + + if (supported_environment_blend_modes != nullptr) { + // free previous results + memfree(supported_environment_blend_modes); + supported_environment_blend_modes = nullptr; + num_supported_environment_blend_modes = 0; + } + + XrResult result = xrEnumerateEnvironmentBlendModes(instance, system_id, view_configuration, 0, &num_supported_environment_blend_modes, nullptr); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to get supported environmental blend mode count [", get_error_string(result), "]"); + return false; + } + + supported_environment_blend_modes = (XrEnvironmentBlendMode *)memalloc(sizeof(XrEnvironmentBlendMode) * num_supported_environment_blend_modes); + ERR_FAIL_NULL_V(supported_environment_blend_modes, false); + + result = xrEnumerateEnvironmentBlendModes(instance, system_id, view_configuration, num_supported_environment_blend_modes, &num_supported_environment_blend_modes, supported_environment_blend_modes); + ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate environmental blend modes"); + + for (uint32_t i = 0; i < num_supported_environment_blend_modes; i++) { + print_verbose(String("OpenXR: Found environmental blend mode ") + OpenXRUtil::get_environment_blend_mode_name(supported_environment_blend_modes[i])); + } + + return true; +} + bool OpenXRAPI::is_view_configuration_supported(XrViewConfigurationType p_configuration_type) const { ERR_FAIL_NULL_V(supported_view_configuration_types, false); @@ -551,6 +582,12 @@ void OpenXRAPI::destroy_instance() { supported_view_configuration_types = nullptr; } + if (supported_environment_blend_modes != nullptr) { + memfree(supported_environment_blend_modes); + supported_environment_blend_modes = nullptr; + num_supported_environment_blend_modes = 0; + } + if (instance != XR_NULL_HANDLE) { for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { wrapper->on_instance_destroyed(); @@ -1205,6 +1242,7 @@ bool OpenXRAPI::resolve_instance_openxr_symbols() { OPENXR_API_INIT_XR_FUNC_V(xrDestroySwapchain); OPENXR_API_INIT_XR_FUNC_V(xrEndFrame); OPENXR_API_INIT_XR_FUNC_V(xrEndSession); + OPENXR_API_INIT_XR_FUNC_V(xrEnumerateEnvironmentBlendModes); OPENXR_API_INIT_XR_FUNC_V(xrEnumerateReferenceSpaces); OPENXR_API_INIT_XR_FUNC_V(xrEnumerateSwapchainFormats); OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurations); @@ -1312,6 +1350,11 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) { return false; } + if (!load_supported_environmental_blend_modes()) { + destroy_instance(); + return false; + } + return true; } @@ -1822,7 +1865,7 @@ void OpenXRAPI::end_frame() { XR_TYPE_FRAME_END_INFO, // type nullptr, // next frame_state.predictedDisplayTime, // displayTime - XR_ENVIRONMENT_BLEND_MODE_OPAQUE, // environmentBlendMode + environment_blend_mode, // environmentBlendMode 0, // layerCount nullptr // layers }; @@ -1874,7 +1917,7 @@ void OpenXRAPI::end_frame() { XR_TYPE_FRAME_END_INFO, // type nullptr, // next frame_state.predictedDisplayTime, // displayTime - XR_ENVIRONMENT_BLEND_MODE_OPAQUE, // environmentBlendMode + environment_blend_mode, // environmentBlendMode static_cast<uint32_t>(layers_list.size()), // layerCount layers_list.ptr() // layers }; @@ -2777,3 +2820,18 @@ void OpenXRAPI::register_composition_layer_provider(OpenXRCompositionLayerProvid void OpenXRAPI::unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider) { composition_layer_providers.erase(provider); } + +const XrEnvironmentBlendMode *OpenXRAPI::get_supported_environment_blend_modes(uint32_t &count) { + count = num_supported_environment_blend_modes; + return supported_environment_blend_modes; +} + +bool OpenXRAPI::set_environment_blend_mode(XrEnvironmentBlendMode mode) { + for (uint32_t i = 0; i < num_supported_environment_blend_modes; i++) { + if (supported_environment_blend_modes[i] == mode) { + environment_blend_mode = mode; + return true; + } + } + return false; +} diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index e1787c6da0..8c642c4ff4 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -99,9 +99,13 @@ private: XrFormFactor form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; XrViewConfigurationType view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_STAGE; - // XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled. + // blend mode + XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + uint32_t num_supported_environment_blend_modes = 0; + XrEnvironmentBlendMode *supported_environment_blend_modes = nullptr; + // state XrInstance instance = XR_NULL_HANDLE; XrSystemId system_id = 0; @@ -182,6 +186,7 @@ private: EXT_PROTO_XRRESULT_FUNC2(xrEndFrame, (XrSession), session, (const XrFrameEndInfo *), frameEndInfo) EXT_PROTO_XRRESULT_FUNC1(xrEndSession, (XrSession), session) EXT_PROTO_XRRESULT_FUNC3(xrEnumerateApiLayerProperties, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrApiLayerProperties *), properties) + EXT_PROTO_XRRESULT_FUNC6(xrEnumerateEnvironmentBlendModes, (XrInstance), instance, (XrSystemId), systemId, (XrViewConfigurationType), viewConfigurationType, (uint32_t), environmentBlendModeCapacityInput, (uint32_t *), environmentBlendModeCountOutput, (XrEnvironmentBlendMode *), environmentBlendModes) EXT_PROTO_XRRESULT_FUNC4(xrEnumerateInstanceExtensionProperties, (const char *), layerName, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrExtensionProperties *), properties) EXT_PROTO_XRRESULT_FUNC4(xrEnumerateReferenceSpaces, (XrSession), session, (uint32_t), spaceCapacityInput, (uint32_t *), spaceCountOutput, (XrReferenceSpaceType *), spaces) EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainFormats, (XrSession), session, (uint32_t), formatCapacityInput, (uint32_t *), formatCountOutput, (int64_t *), formats) @@ -210,6 +215,7 @@ private: bool create_instance(); bool get_system_info(); bool load_supported_view_configuration_types(); + bool load_supported_environmental_blend_modes(); bool is_view_configuration_supported(XrViewConfigurationType p_configuration_type) const; bool load_supported_view_configuration_views(XrViewConfigurationType p_configuration_type); void destroy_instance(); @@ -390,6 +396,9 @@ public: void register_composition_layer_provider(OpenXRCompositionLayerProvider *provider); void unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider); + const XrEnvironmentBlendMode *get_supported_environment_blend_modes(uint32_t &count); + bool set_environment_blend_mode(XrEnvironmentBlendMode mode); + OpenXRAPI(); ~OpenXRAPI(); }; diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 702e56b410..51de9b913a 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -870,6 +870,60 @@ void OpenXRInterface::stop_passthrough() { } } +Array OpenXRInterface::get_supported_environment_blend_modes() { + Array modes; + + if (!openxr_api) { + return modes; + } + + uint32_t count = 0; + const XrEnvironmentBlendMode *env_blend_modes = openxr_api->get_supported_environment_blend_modes(count); + + if (!env_blend_modes) { + return modes; + } + + for (uint32_t i = 0; i < count; i++) { + switch (env_blend_modes[i]) { + case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: + modes.push_back(XR_ENV_BLEND_MODE_OPAQUE); + break; + case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: + modes.push_back(XR_ENV_BLEND_MODE_ADDITIVE); + break; + case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: + modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND); + break; + default: + WARN_PRINT("Unsupported blend mode found: " + String::num_int64(int64_t(env_blend_modes[i]))); + } + } + return modes; +} + +bool OpenXRInterface::set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) { + if (openxr_api) { + XrEnvironmentBlendMode oxr_blend_mode; + switch (mode) { + case XR_ENV_BLEND_MODE_OPAQUE: + oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + break; + case XR_ENV_BLEND_MODE_ADDITIVE: + oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE; + break; + case XR_ENV_BLEND_MODE_ALPHA_BLEND: + oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND; + break; + default: + WARN_PRINT("Unknown blend mode requested: " + String::num_int64(int64_t(mode))); + oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + } + return openxr_api->set_environment_blend_mode(oxr_blend_mode); + } + return false; +} + void OpenXRInterface::on_state_ready() { emit_signal(SNAME("session_begun")); } diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index cce329d8e6..40ee95f02f 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -147,6 +147,10 @@ public: virtual bool start_passthrough() override; virtual void stop_passthrough() override; + /** environment blend mode. */ + virtual Array get_supported_environment_blend_modes() override; + virtual bool set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) override; + void on_state_ready(); void on_state_visible(); void on_state_focused(); diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp index 50ebf468b9..926e918390 100644 --- a/modules/openxr/openxr_util.cpp +++ b/modules/openxr/openxr_util.cpp @@ -29,267 +29,38 @@ /**************************************************************************/ #include "openxr_util.h" +#include <openxr/openxr_reflection.h> -#define ENUM_TO_STRING_CASE(e) \ - case e: { \ - return String(#e); \ - } break; +#define XR_ENUM_CASE_STR(name, val) \ + case name: \ + return #name; +#define XR_ENUM_SWITCH(enumType, var) \ + switch (var) { \ + XR_LIST_ENUM_##enumType(XR_ENUM_CASE_STR) default : return "Unknown " #enumType ": " + String::num_int64(int64_t(var)); \ + } -// TODO see if we can generate this code further using the xml file with meta data supplied by OpenXR +String OpenXRUtil::get_view_configuration_name(XrViewConfigurationType p_view_configuration){ + XR_ENUM_SWITCH(XrViewConfigurationType, p_view_configuration) +} -String OpenXRUtil::get_view_configuration_name(XrViewConfigurationType p_view_configuration) { - switch (p_view_configuration) { - ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO) - ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO) - ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO) - ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT) - ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM) - default: { - return String("View Configuration ") + String::num_int64(int64_t(p_view_configuration)); - } break; - } +String OpenXRUtil::get_reference_space_name(XrReferenceSpaceType p_reference_space){ + XR_ENUM_SWITCH(XrReferenceSpaceType, p_reference_space) } -String OpenXRUtil::get_reference_space_name(XrReferenceSpaceType p_reference_space) { - switch (p_reference_space) { - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_VIEW) - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_LOCAL) - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_STAGE) - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT) - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO) - ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_MAX_ENUM) - default: { - return String("Reference space ") + String::num_int64(int64_t(p_reference_space)); - } break; - } +String OpenXRUtil::get_structure_type_name(XrStructureType p_structure_type){ + XR_ENUM_SWITCH(XrStructureType, p_structure_type) } -String OpenXRUtil::get_structure_type_name(XrStructureType p_structure_type) { - switch (p_structure_type) { - ENUM_TO_STRING_CASE(XR_TYPE_UNKNOWN) - ENUM_TO_STRING_CASE(XR_TYPE_API_LAYER_PROPERTIES) - ENUM_TO_STRING_CASE(XR_TYPE_EXTENSION_PROPERTIES) - ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_GET_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_PROPERTIES) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_LOCATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW) - ENUM_TO_STRING_CASE(XR_TYPE_SESSION_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SESSION_BEGIN_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_STATE) - ENUM_TO_STRING_CASE(XR_TYPE_FRAME_END_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_HAPTIC_VIBRATION) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_BUFFER) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_BOOLEAN) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_FLOAT) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_VECTOR2F) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_POSE) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_SET_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_PROPERTIES) - ENUM_TO_STRING_CASE(XR_TYPE_FRAME_WAIT_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PROJECTION) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_QUAD) - ENUM_TO_STRING_CASE(XR_TYPE_REFERENCE_SPACE_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_SPACE_CREATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_VIEW) - ENUM_TO_STRING_CASE(XR_TYPE_SPACE_LOCATION) - ENUM_TO_STRING_CASE(XR_TYPE_SPACE_VELOCITY) - ENUM_TO_STRING_CASE(XR_TYPE_FRAME_STATE) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_PROPERTIES) - ENUM_TO_STRING_CASE(XR_TYPE_FRAME_BEGIN_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_EVENTS_LOST) - ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) - ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_STATE) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_GET_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_HAPTIC_ACTION_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_ACTIONS_SYNC_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_SWAPCHAIN_FORMAT_LIST_CREATE_INFO_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_LABEL_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_XCB_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_D3D11_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_D3D12_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_EYE_GAZE_SAMPLE_TIME_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_VISIBILITY_MASK_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_MAIN_SESSION_VISIBILITY_CHANGED_EXTX) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_EGL_MNDX) - ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINT_LOCATIONS_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINT_VELOCITIES_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_HAND_TRACKING_MESH_PROPERTIES_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_SPACE_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_UPDATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_POSE_TYPE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SESSION_BEGIN_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_STATE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_STATE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SWAPCHAIN_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_STATE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC) - ENUM_TO_STRING_CASE(XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB) - ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT) - ENUM_TO_STRING_CASE(XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBJECTS_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_PLANES_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESHES_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_BUFFERS_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB) - ENUM_TO_STRING_CASE(XR_TYPE_VIVE_TRACKER_PATHS_HTCX) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_MESH_FB) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_SCALE_FB) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_AIM_STATE_FB) - ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB) - ENUM_TO_STRING_CASE(XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB) - ENUM_TO_STRING_CASE(XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB) - ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) - ENUM_TO_STRING_CASE(XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB) - ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_STYLE_FB) - ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB) - ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB) - ENUM_TO_STRING_CASE(XR_TYPE_BINDING_MODIFICATIONS_KHR) - ENUM_TO_STRING_CASE(XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) - ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) - ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB) - ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB) - ENUM_TO_STRING_CASE(XR_STRUCTURE_TYPE_MAX_ENUM) - default: { - return String("Structure type ") + String::num_int64(int64_t(p_structure_type)); - } break; - } +String OpenXRUtil::get_session_state_name(XrSessionState p_session_state){ + XR_ENUM_SWITCH(XrSessionState, p_session_state) } -String OpenXRUtil::get_session_state_name(XrSessionState p_session_state) { - switch (p_session_state) { - ENUM_TO_STRING_CASE(XR_SESSION_STATE_UNKNOWN) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_IDLE) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_READY) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_SYNCHRONIZED) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_VISIBLE) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_FOCUSED) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_STOPPING) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_LOSS_PENDING) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_EXITING) - ENUM_TO_STRING_CASE(XR_SESSION_STATE_MAX_ENUM) - default: { - return String("Session state ") + String::num_int64(int64_t(p_session_state)); - } break; - } +String OpenXRUtil::get_action_type_name(XrActionType p_action_type){ + XR_ENUM_SWITCH(XrActionType, p_action_type) } -String OpenXRUtil::get_action_type_name(XrActionType p_action_type) { - switch (p_action_type) { - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_BOOLEAN_INPUT) - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_FLOAT_INPUT) - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_VECTOR2F_INPUT) - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_POSE_INPUT) - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_VIBRATION_OUTPUT) - ENUM_TO_STRING_CASE(XR_ACTION_TYPE_MAX_ENUM) - default: { - return String("Action type ") + String::num_int64(int64_t(p_action_type)); - } break; - } +String OpenXRUtil::get_environment_blend_mode_name(XrEnvironmentBlendMode p_blend_mode) { + XR_ENUM_SWITCH(XrEnvironmentBlendMode, p_blend_mode); } String OpenXRUtil::make_xr_version_string(XrVersion p_version) { diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h index dfda537474..7e7a6a1880 100644 --- a/modules/openxr/openxr_util.h +++ b/modules/openxr/openxr_util.h @@ -41,6 +41,7 @@ public: static String get_structure_type_name(XrStructureType p_structure_type); static String get_session_state_name(XrSessionState p_session_state); static String get_action_type_name(XrActionType p_action_type); + static String get_environment_blend_mode_name(XrEnvironmentBlendMode p_blend_mode); static String make_xr_version_string(XrVersion p_version); }; diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp index 306a2f1bbd..c39e49387a 100644 --- a/modules/openxr/register_types.cpp +++ b/modules/openxr/register_types.cpp @@ -52,6 +52,7 @@ #include "extensions/openxr_htc_controller_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" #include "extensions/openxr_huawei_controller_extension.h" +#include "extensions/openxr_ml2_controller_extension.h" #include "extensions/openxr_palm_pose_extension.h" #include "extensions/openxr_pico_controller_extension.h" #include "extensions/openxr_wmr_controller_extension.h" @@ -102,6 +103,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) { OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension)); OpenXRAPI::register_extension_wrapper(memnew(OpenXRWMRControllerExtension)); + OpenXRAPI::register_extension_wrapper(memnew(OpenXRML2ControllerExtension)); } if (OpenXRAPI::openxr_is_enabled()) { diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index 36d0b41889..9224760c5b 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -128,7 +128,6 @@ void WebRTCMultiplayerPeer::poll() { // Server connected. connection_status = CONNECTION_CONNECTED; emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER); - emit_signal(SNAME("connection_succeeded")); } else { emit_signal(SNAME("peer_connected"), E); } diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 389d8c56ad..c12fc5e834 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -237,7 +237,6 @@ void WebSocketMultiplayerPeer::_poll_client() { } connection_status = CONNECTION_CONNECTED; emit_signal("peer_connected", 1); - emit_signal("connection_succeeded"); } else { return; // Still waiting for an ID. } |