diff options
Diffstat (limited to 'modules')
25 files changed, 150 insertions, 85 deletions
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp index 216fa6c9a2..fc1f01ae50 100644 --- a/modules/basis_universal/image_compress_basisu.cpp +++ b/modules/basis_universal/image_compress_basisu.cpp @@ -120,7 +120,8 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha Vector<uint32_t> mip_data_padded; for (int32_t i = 0; i <= image->get_mipmap_count(); i++) { - int ofs, size, width, height; + int64_t ofs, size; + int width, height; image->get_mipmap_offset_size_and_dimensions(i, ofs, size, width, height); const uint8_t *image_mip_data = image_data.ptr() + ofs; diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index ebd1577172..84534fec06 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -51,7 +51,7 @@ <param index="3" name="data" type="int" default="0" /> <description> Initiates a connection to a foreign [param address] using the specified [param port] and allocating the requested [param channels]. Optional [param data] can be passed during connection in the form of a 32 bit integer. - [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method. + [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] on both ends before calling this method. </description> </method> <method name="create_host"> @@ -61,7 +61,9 @@ <param index="2" name="in_bandwidth" type="int" default="0" /> <param index="3" name="out_bandwidth" type="int" default="0" /> <description> - Create an ENetHost that will allow up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth]. + Creates an ENetHost that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero). + This method binds a random available dynamic UDP port on the host machine at the [i]unspecified[/i] address. Use [method create_host_bound] to specify the address and port. + [b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection. </description> </method> <method name="create_host_bound"> @@ -73,7 +75,8 @@ <param index="4" name="in_bandwidth" type="int" default="0" /> <param index="5" name="out_bandwidth" type="int" default="0" /> <description> - Create an ENetHost like [method create_host] which is also bound to the given [param bind_address] and [param bind_port]. + Creates an ENetHost bound to the given [param bind_address] and [param bind_port] that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero). + [b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection. </description> </method> <method name="destroy"> @@ -141,8 +144,9 @@ <return type="Array" /> <param index="0" name="timeout" type="int" default="0" /> <description> - Waits for events on the specified host and shuttles packets between the host and its peers, with the given [param timeout] (in milliseconds). The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer]. + Waits for events on this connection and shuttles packets between the host and its peers, with the given [param timeout] (in milliseconds). The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer]. Call this function regularly to handle connections, disconnections, and to receive new packets. + [b]Note:[/b] This method must be called on both ends involved in the event (sending and receiving hosts). </description> </method> <method name="socket_send"> diff --git a/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp b/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp index afb63246e4..daa4db37df 100644 --- a/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp +++ b/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp @@ -126,10 +126,9 @@ Node *EditorSceneFormatImporterFBX2GLTF::import_scene(const String &p_path, uint Variant EditorSceneFormatImporterFBX2GLTF::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) { - if (p_option == "fbx/embedded_image_handling") { - return false; - } - if (p_options.has("fbx/importer") && int(p_options["fbx/importer"]) == EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF && p_option == "fbx/embedded_image_handling") { + // Remove all the FBX options except for 'fbx/importer' if the importer is fbx2gltf. + // These options are available only for ufbx. + if (p_option.begins_with("fbx/") && p_option != "fbx/importer" && p_options.has("fbx/importer") && int(p_options["fbx/importer"]) == EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF) { return false; } return true; diff --git a/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp index fb5e324390..4d5f220539 100644 --- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp +++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp @@ -76,9 +76,6 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) { state->set_import_as_skeleton_bones(true); } - if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) { - state->set_import_as_skeleton_bones(true); - } p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; state->set_bake_fps(p_options["animation/fps"]); Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir()); @@ -93,21 +90,17 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t Variant EditorSceneFormatImporterUFBX::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) { - String file_extension = p_path.get_extension().to_lower(); - if (file_extension != "fbx" && p_option.begins_with("fbx/")) { - return false; - } - if ((file_extension != "gltf" && file_extension != "glb") && p_option.begins_with("gltf/")) { - return false; - } return true; } void EditorSceneFormatImporterUFBX::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) { - r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/importer", PROPERTY_HINT_ENUM, "ufbx,FBX2glTF"), FBX_IMPORTER_UFBX)); - r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::BOOL, "fbx/allow_geometry_helper_nodes"), false)); - r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/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), FBXState::HANDLE_BINARY_EXTRACT_TEXTURES)); + // Returns all the options when path is empty because that means it's for the Project Settings. + if (p_path.is_empty() || p_path.get_extension().to_lower() == "fbx") { + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/importer", PROPERTY_HINT_ENUM, "ufbx,FBX2glTF"), FBX_IMPORTER_UFBX)); + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::BOOL, "fbx/allow_geometry_helper_nodes"), false)); + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "fbx/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), FBXState::HANDLE_BINARY_EXTRACT_TEXTURES)); + } } void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 73b1e44db3..eaf2565e69 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1537,10 +1537,14 @@ void GDScript::clear(ClearData *p_clear_data) { } } - RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); - for (GDScript *E : must_clear_dependencies) { - clear_data->scripts.insert(E); - E->clear(clear_data); + // If we're in the process of shutting things down then every single script will be cleared + // anyway, so we can safely skip this very costly operation. + if (!GDScriptLanguage::singleton->finishing) { + RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); + for (GDScript *E : must_clear_dependencies) { + clear_data->scripts.insert(E); + E->clear(clear_data); + } } for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) { @@ -2246,6 +2250,11 @@ String GDScriptLanguage::get_extension() const { } void GDScriptLanguage::finish() { + if (finishing) { + return; + } + finishing = true; + _call_stack.free(); // Clear the cache before parsing the script_list @@ -2281,6 +2290,8 @@ void GDScriptLanguage::finish() { } script_list.clear(); function_list.clear(); + + finishing = false; } void GDScriptLanguage::profiling_start() { diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d097cb193b..4e78fbe302 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -411,6 +411,8 @@ class GDScriptLanguage : public ScriptLanguage { static GDScriptLanguage *singleton; + bool finishing = false; + Variant *_global_array = nullptr; Vector<Variant> global_array; HashMap<StringName, int> globals; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 5469dad3f7..b0ac4aa800 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1064,12 +1064,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Get at (potential) root stack pos, so it can be returned. GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base); + if (r_error) { return GDScriptCodeGenerator::Address(); } GDScriptCodeGenerator::Address prev_base = base; + // In case the base has a setter, don't use the address directly, as we want to call that setter. + // So use a temp value instead and call the setter at the end. + GDScriptCodeGenerator::Address base_temp; + if (base.mode == GDScriptCodeGenerator::Address::MEMBER && member_property_has_setter && !member_property_is_in_setter) { + base_temp = codegen.add_temporary(base.type); + gen->write_assign(base_temp, base); + prev_base = base_temp; + } + struct ChainInfo { bool is_named = false; GDScriptCodeGenerator::Address base; @@ -1218,6 +1228,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->write_end_jump_if_shared(); } } + } else if (base_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + // Save the temp value back to the base by calling its setter. + gen->write_call(GDScriptCodeGenerator::Address(), base, member_property_setter_function, { assigned }); } if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index b58b44973e..b1ffc02e4b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1521,22 +1521,19 @@ static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, G } if (scr.is_valid()) { ci.type.script_path = scr->get_path(); + ci.type.script_type = scr; + ci.type.native_type = scr->get_instance_base_type(); + ci.type.kind = GDScriptParser::DataType::SCRIPT; if (scr->get_path().ends_with(".gd")) { - Error err; - Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(scr->get_path(), GDScriptParserRef::INTERFACE_SOLVED, err); - if (err == OK) { + Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(scr->get_path()); + if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) { ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; ci.type.class_type = parser->get_parser()->get_tree(); ci.type.kind = GDScriptParser::DataType::CLASS; - p_context.dependent_parsers.push_back(parser); return ci; } } - - ci.type.kind = GDScriptParser::DataType::SCRIPT; - ci.type.script_type = scr; - ci.type.native_type = scr->get_instance_base_type(); } else { ci.type.kind = GDScriptParser::DataType::NATIVE; } @@ -1811,8 +1808,6 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, if (mb && mb->is_const()) { bool all_is_const = true; Vector<Variant> args; - GDScriptParser::CompletionContext c2 = p_context; - c2.current_line = call->start_line; for (int i = 0; all_is_const && i < call->arguments.size(); i++) { GDScriptCompletionIdentifier arg; @@ -1849,16 +1844,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } if (FileAccess::exists(script)) { - Error err = OK; - Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err); - if (err == OK) { + Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script); + if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) { r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.script_path = script; r_type.type.class_type = parser->get_parser()->get_tree(); r_type.type.is_constant = false; r_type.type.kind = GDScriptParser::DataType::CLASS; r_type.value = Variant(); - p_context.dependent_parsers.push_back(parser); found = true; } } @@ -2310,9 +2303,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, if (ScriptServer::is_global_class(p_identifier->name)) { String script = ScriptServer::get_global_class_path(p_identifier->name); if (script.to_lower().ends_with(".gd")) { - Error err = OK; - Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err); - if (err == OK) { + Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script); + if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) { r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.script_path = script; r_type.type.class_type = parser->get_parser()->get_tree(); @@ -2320,7 +2312,6 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, r_type.type.is_constant = false; r_type.type.kind = GDScriptParser::DataType::CLASS; r_type.value = Variant(); - p_context.dependent_parsers.push_back(parser); return true; } } else { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 433f767f1e..54bb152f7f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -260,6 +260,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node context.current_line = tokenizer->get_cursor_line(); context.current_argument = p_argument; context.node = p_node; + context.parser = this; completion_context = context; } @@ -277,6 +278,7 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Typ context.current_suite = current_suite; context.current_line = tokenizer->get_cursor_line(); context.builtin_type = p_builtin_type; + context.parser = this; completion_context = context; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 21942222cf..60ee477656 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1314,7 +1314,7 @@ public: Variant::Type builtin_type = Variant::VARIANT_MAX; Node *node = nullptr; Object *base = nullptr; - List<Ref<GDScriptParserRef>> dependent_parsers; + GDScriptParser *parser = nullptr; }; struct CompletionCall { diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 5b1639e250..404b61fb40 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -574,7 +574,9 @@ GDScriptTokenizer::Token GDScriptTokenizerText::potential_identifier() { if (len == 1 && _peek(-1) == '_') { // Lone underscore. - return make_token(Token::UNDERSCORE); + Token token = make_token(Token::UNDERSCORE); + token.literal = "_"; + return token; } String name(_start, len); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 912367764b..5eab8a6306 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1807,7 +1807,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else if (methodstr == "free") { if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { if (base->is_ref_counted()) { - err_text = "Attempted to free a reference."; + err_text = "Attempted to free a RefCounted object."; OPCODE_BREAK; } else if (base->get_type() == Variant::OBJECT) { err_text = "Attempted to free a locked object (calling or emitting)."; @@ -1898,7 +1898,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else if (methodstr == "free") { if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) { if (base->is_ref_counted()) { - err_text = "Attempted to free a reference."; + err_text = "Attempted to free a RefCounted object."; OPCODE_BREAK; } else if (base->get_type() == Variant::OBJECT) { err_text = "Attempted to free a locked object (calling or emitting)."; diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd new file mode 100644 index 0000000000..9e27a500bf --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd @@ -0,0 +1,13 @@ +# https://github.com/godotengine/godot/issues/85952 + +var vec: Vector2 = Vector2.ZERO: + set(new_vec): + prints("setting vec from", vec, "to", new_vec) + if new_vec == Vector2(1, 1): + vec = new_vec + +func test(): + vec.x = 2 + vec.y = 2 + + prints("vec is", vec) diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out new file mode 100644 index 0000000000..31b3b3a3a8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out @@ -0,0 +1,4 @@ +GDTEST_OK +setting vec from (0, 0) to (2, 0) +setting vec from (0, 0) to (0, 2) +vec is (0, 0) diff --git a/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd new file mode 100644 index 0000000000..b07c40b6da --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.gd @@ -0,0 +1,15 @@ +extends Node + +func test() -> void: + var node1 := Node.new() + node1.name = "_" + var node2 := Node.new() + node2.name = "Child" + var node3 := Node.new() + node3.name = "Child" + + add_child(node1) + node1.add_child(node2) + add_child(node3) + + assert(get_node("_/Child") == $_/Child) diff --git a/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/single_underscore_node_name.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index b474128fd6..f70e440781 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -332,7 +332,8 @@ Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_pa } void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) { - if (p_path.get_extension().to_lower() != "blend") { + // Returns all the options when path is empty because that means it's for the Project Settings. + if (!p_path.is_empty() && p_path.get_extension().to_lower() != "blend") { return; } #define ADD_OPTION_BOOL(PATH, VALUE) \ diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index b38c64de01..ce7e17d361 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -84,8 +84,12 @@ 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, "gltf/naming_version", PROPERTY_HINT_ENUM, "Godot 4.1 or 4.0,Godot 4.2 or later"), 1)); - 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)); + String file_extension = p_path.get_extension().to_lower(); + // Returns all the options when path is empty because that means it's for the Project Settings. + if (p_path.is_empty() || file_extension == "gltf" || file_extension == "glb") { + r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/naming_version", PROPERTY_HINT_ENUM, "Godot 4.1 or 4.0,Godot 4.2 or later"), 1)); + 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)); + } } void EditorSceneFormatImporterGLTF::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const { @@ -98,10 +102,6 @@ void EditorSceneFormatImporterGLTF::handle_compatibility_options(HashMap<StringN Variant EditorSceneFormatImporterGLTF::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) { - String file_extension = p_path.get_extension().to_lower(); - if ((file_extension != "gltf" && file_extension != "glb") && p_option.begins_with("gltf/")) { - return false; - } return true; } diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index c0232e6d0c..dff1e62e82 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -789,8 +789,9 @@ Error GLTFDocument::_parse_buffers(Ref<GLTFState> p_state, const String &p_base_ ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER); uri = uri.uri_decode(); uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows. + ERR_FAIL_COND_V_MSG(!FileAccess::exists(uri), ERR_FILE_NOT_FOUND, "glTF: Binary file not found: " + uri); buffer_data = FileAccess::get_file_as_bytes(uri); - ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri); + ERR_FAIL_COND_V_MSG(buffer_data.is_empty(), ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri); } ERR_FAIL_COND_V(!buffer.has("byteLength"), ERR_PARSE_ERROR); diff --git a/modules/interactive_music/audio_stream_interactive.cpp b/modules/interactive_music/audio_stream_interactive.cpp index 01764d66ed..d7762295eb 100644 --- a/modules/interactive_music/audio_stream_interactive.cpp +++ b/modules/interactive_music/audio_stream_interactive.cpp @@ -976,6 +976,8 @@ void AudioStreamPlaybackInteractive::switch_to_clip_by_name(const StringName &p_ return; } + ERR_FAIL_COND_MSG(stream.is_null(), "Attempted to switch while not playing back any stream."); + for (int i = 0; i < stream->get_clip_count(); i++) { if (stream->get_clip_name(i) == p_name) { switch_request = i; diff --git a/modules/interactive_music/register_types.cpp b/modules/interactive_music/register_types.cpp index 5baea13f81..6175ea6493 100644 --- a/modules/interactive_music/register_types.cpp +++ b/modules/interactive_music/register_types.cpp @@ -42,11 +42,11 @@ void initialize_interactive_music_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { GDREGISTER_CLASS(AudioStreamPlaylist); - GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackPlaylist); + GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPlaylist); GDREGISTER_CLASS(AudioStreamInteractive); - GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackInteractive); + GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackInteractive); GDREGISTER_CLASS(AudioStreamSynchronized); - GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackSynchronized); + GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackSynchronized); } #ifdef TOOLS_ENABLED if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 7ac7bd8088..33b0b0d015 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -233,14 +233,14 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ MeshInstance &mi = mesh_instances.write[m_i]; Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height()); sizes.push_back(s); - atlas_size = atlas_size.max(s + Size2i(2, 2)); + atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range)); } int max = nearest_power_of_2_templated(atlas_size.width); max = MAX(max, nearest_power_of_2_templated(atlas_size.height)); if (max > p_max_texture_size) { - return BAKE_ERROR_LIGHTMAP_TOO_SMALL; + return BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE; } if (p_step_function) { @@ -254,19 +254,27 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ int best_atlas_memory = 0x7FFFFFFF; Vector<Vector3i> best_atlas_offsets; - //determine best texture array atlas size by bruteforce fitting + // Determine best texture array atlas size by bruteforce fitting. while (atlas_size.x <= p_max_texture_size && atlas_size.y <= p_max_texture_size) { Vector<Vector2i> source_sizes; Vector<int> source_indices; source_sizes.resize(sizes.size()); source_indices.resize(sizes.size()); for (int i = 0; i < source_indices.size(); i++) { - source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps + source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps. source_indices.write[i] = i; } Vector<Vector3i> atlas_offsets; atlas_offsets.resize(source_sizes.size()); + // Ensure the sizes can all fit into a single atlas layer. + // This should always happen, and this check is only in place to prevent an infinite loop. + for (int i = 0; i < source_sizes.size(); i++) { + if (source_sizes[i] > atlas_size) { + return BAKE_ERROR_ATLAS_TOO_SMALL; + } + } + int slices = 0; while (source_sizes.size() > 0) { diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index 51e6c3e277..a353073f21 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -248,32 +248,27 @@ void OpenXRActionMapEditor::_on_interaction_profile_selected(const String p_path void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) { Error err = OK; - action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); - if (err != OK) { - if ((err == ERR_FILE_NOT_FOUND || err == ERR_CANT_OPEN) && p_create_new_if_missing) { - action_map.instantiate(); - action_map->create_default_action_sets(); - - // Save it immediately - err = ResourceSaver::save(action_map, p_path); - if (err != OK) { - // Show warning but continue. - EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err])); - } else { - // Reload so it's cached. - action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); - if (err != OK) { - // Show warning but continue. - EditorNode::get_singleton()->show_warning(vformat(TTR("Error reloading file %s: %s"), edited_path, error_names[err])); - } - } - } else { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da->file_exists(p_path)) { + action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + if (err != OK) { EditorNode::get_singleton()->show_warning(vformat(TTR("Error loading %s: %s."), edited_path, error_names[err])); edited_path = ""; header_label->set_text(""); return; } + } else if (p_create_new_if_missing) { + action_map.instantiate(); + action_map->create_default_action_sets(); + action_map->set_path(p_path); + + // Save it immediately + err = ResourceSaver::save(action_map, p_path); + if (err != OK) { + // Show warning but continue. + EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err])); + } } edited_path = p_path; diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index fbbc61a91c..cce9c09361 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -651,6 +651,10 @@ bool OpenXRInterface::initialize() { // make this our primary interface xr_server->set_primary_interface(this); + // Register an additional output with the display server, so rendering won't + // be skipped if no windows are visible. + DisplayServer::get_singleton()->register_additional_output(this); + initialized = true; return initialized; @@ -674,6 +678,8 @@ void OpenXRInterface::uninitialize() { } } + DisplayServer::get_singleton()->unregister_additional_output(this); + initialized = false; } diff --git a/modules/squish/image_decompress_squish.cpp b/modules/squish/image_decompress_squish.cpp index fba76621d6..72640cd936 100644 --- a/modules/squish/image_decompress_squish.cpp +++ b/modules/squish/image_decompress_squish.cpp @@ -77,10 +77,11 @@ void image_decompress_squish(Image *p_image) { } for (int i = 0; i <= mm_count; i++) { - int src_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0; + int64_t src_ofs = 0, mipmap_size = 0; + int mipmap_w = 0, mipmap_h = 0; p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h); - int dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i); + int64_t dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i); squish::DecompressImage(&wb[dst_ofs], w, h, &rb[src_ofs], squish_flags); w >>= 1; |
