diff options
36 files changed, 434 insertions, 26 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index c812996fd4..01f2404866 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -130,7 +130,7 @@ jobs: - name: Generate C# glue if: ${{ matrix.build-mono }} run: | - ${{ matrix.bin }} --headless --generate-mono-glue ./modules/mono/glue || true + ${{ matrix.bin }} --headless --generate-mono-glue ./modules/mono/glue - name: Build .NET solutions if: ${{ matrix.build-mono }} diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index d5a4c146e0..ee55288783 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -21,6 +21,13 @@ [b]Note:[/b] When using threaded rendering this method synchronizes the rendering thread. Calling it often may have a negative impact on performance. </description> </method> + <method name="convert_from_particles"> + <return type="void" /> + <param index="0" name="particles" type="Node" /> + <description> + Sets this node's properties to match a given [CPUParticles2D] node. + </description> + </method> <method name="emit_particle"> <return type="void" /> <param index="0" name="xform" type="Transform2D" /> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 3310113b61..9ba64feebc 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -19,6 +19,13 @@ Returns the axis-aligned bounding box that contains all the particles that are active in the current frame. </description> </method> + <method name="convert_from_particles"> + <return type="void" /> + <param index="0" name="particles" type="Node" /> + <description> + Sets this node's properties to match a given [CPUParticles3D] node. + </description> + </method> <method name="emit_particle"> <return type="void" /> <param index="0" name="xform" type="Transform3D" /> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index f426030f98..0e35bc7031 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -492,6 +492,9 @@ <member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await. </member> + <member name="debug/gdscript/warnings/redundant_for_variable_type" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a [code]for[/code] variable type specifier is a supertype of the inferred type. + </member> <member name="debug/gdscript/warnings/redundant_static_unload" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables. </member> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 59b1d176c6..b955fc2fca 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -2622,7 +2622,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co vkCmdCopyBufferToImage(command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy); - staging_buffer_blocks.write[staging_buffer_current].fill_amount += alloc_size; + staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; } } } diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 7d7b156bd0..c5a667cd5f 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -204,6 +204,7 @@ PluginConfigDialog::PluginConfigDialog() { name_edit = memnew(LineEdit); name_edit->set_placeholder("MyPlugin"); + name_edit->set_tooltip_text(TTR("Required. This name will be displayed in the list of plugins.")); name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(name_edit); @@ -215,6 +216,7 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); + subfolder_edit->set_tooltip_text(TTR("Optional. The folder name should generally use `snake_case` naming (avoid spaces and special characters).\nIf left empty, the folder will be named after the plugin name converted to `snake_case`.")); subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(subfolder_edit); @@ -225,6 +227,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(desc_lb); desc_edit = memnew(TextEdit); + desc_edit->set_tooltip_text(TTR("Optional. This description should be kept relatively short (up to 5 lines).\nIt will display when hovering the plugin in the list of plugins.")); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -239,6 +242,7 @@ PluginConfigDialog::PluginConfigDialog() { author_edit = memnew(LineEdit); author_edit->set_placeholder("Godette"); + author_edit->set_tooltip_text(TTR("Optional. The author's username, full name, or organization name.")); author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(author_edit); @@ -249,6 +253,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(version_lb); version_edit = memnew(LineEdit); + version_edit->set_tooltip_text(TTR("Optional. A human-readable version identifier used for informational purposes only.")); version_edit->set_placeholder("1.0"); version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(version_edit); @@ -260,6 +265,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(script_option_lb); script_option_edit = memnew(OptionButton); + script_option_edit->set_tooltip_text(TTR("Required. The scripting language to use for the script.\nNote that a plugin may use several languages at once by adding more scripts to the plugin.")); int default_lang = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); @@ -278,6 +284,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(script_lb); script_edit = memnew(LineEdit); + script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\".")); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index 7be0d6c172..3ac9fee03f 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -33,8 +33,11 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/scene_tree_dock.h" #include "scene/2d/cpu_particles_2d.h" +#include "scene/2d/gpu_particles_2d.h" #include "scene/gui/check_box.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -67,14 +70,26 @@ void CPUParticles2DEditorPlugin::_menu_callback(int p_idx) { switch (p_idx) { case MENU_LOAD_EMISSION_MASK: { file->popup_file_dialog(); - } break; case MENU_CLEAR_EMISSION_MASK: { emission_mask->popup_centered(); } break; case MENU_RESTART: { particles->restart(); - } + } break; + case MENU_CONVERT_TO_GPU_PARTICLES: { + GPUParticles2D *gpu_particles = memnew(GPUParticles2D); + gpu_particles->convert_from_particles(particles); + gpu_particles->set_name(particles->get_name()); + gpu_particles->set_transform(particles->get_transform()); + gpu_particles->set_visible(particles->is_visible()); + gpu_particles->set_process_mode(particles->get_process_mode()); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Convert to GPUParticles3D")); + SceneTreeDock::get_singleton()->replace_node(particles, gpu_particles); + ur->commit_action(false); + } break; } } @@ -257,6 +272,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() { menu = memnew(MenuButton); menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); + menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES); menu->set_text(TTR("CPUParticles2D")); menu->set_switch_on_hover(true); toolbar->add_child(menu); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h index ff8e171208..5077827ce8 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.h +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -49,7 +49,8 @@ class CPUParticles2DEditorPlugin : public EditorPlugin { enum { MENU_LOAD_EMISSION_MASK, MENU_CLEAR_EMISSION_MASK, - MENU_RESTART + MENU_RESTART, + MENU_CONVERT_TO_GPU_PARTICLES, }; enum EmissionMode { diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index 6edfc2ef2e..1f1bc0e561 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -31,8 +31,10 @@ #include "cpu_particles_3d_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/gui/scene_tree_editor.h" #include "editor/plugins/node_3d_editor_plugin.h" +#include "editor/scene_tree_dock.h" #include "scene/gui/menu_button.h" void CPUParticles3DEditor::_node_removed(Node *p_node) { @@ -59,6 +61,20 @@ void CPUParticles3DEditor::_menu_option(int p_option) { case MENU_OPTION_RESTART: { node->restart(); + } break; + + case MENU_OPTION_CONVERT_TO_GPU_PARTICLES: { + GPUParticles3D *gpu_particles = memnew(GPUParticles3D); + gpu_particles->convert_from_particles(node); + gpu_particles->set_name(node->get_name()); + gpu_particles->set_transform(node->get_transform()); + gpu_particles->set_visible(node->is_visible()); + gpu_particles->set_process_mode(node->get_process_mode()); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Convert to GPUParticles3D")); + SceneTreeDock::get_singleton()->replace_node(node, gpu_particles); + ur->commit_action(false); } break; } @@ -102,6 +118,7 @@ CPUParticles3DEditor::CPUParticles3DEditor() { options->set_text(TTR("CPUParticles3D")); options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES); options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles3DEditor::_menu_option)); } diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h index 894e0dfb31..6de23fc2b8 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.h +++ b/editor/plugins/cpu_particles_3d_editor_plugin.h @@ -40,8 +40,8 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase { enum Menu { MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, MENU_OPTION_CLEAR_EMISSION_VOLUME, - MENU_OPTION_RESTART - + MENU_OPTION_RESTART, + MENU_OPTION_CONVERT_TO_GPU_PARTICLES, }; CPUParticles3D *node = nullptr; diff --git a/modules/dds/image_loader_dds.cpp b/modules/dds/image_loader_dds.cpp index 42c8120595..13ca1e6bff 100644 --- a/modules/dds/image_loader_dds.cpp +++ b/modules/dds/image_loader_dds.cpp @@ -66,7 +66,6 @@ enum DDSFormat { DDS_BGR5A1, DDS_BGR565, DDS_BGR10A2, - DDS_INDEXED, DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, DDS_MAX diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 9f9accf507..214b484b12 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2001,13 +2001,16 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } GDScriptParser::DataType variable_type; + String list_visible_type = "<unresolved type>"; if (list_resolved) { variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; variable_type.kind = GDScriptParser::DataType::BUILTIN; variable_type.builtin_type = Variant::INT; + list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type. } else if (p_for->list) { resolve_node(p_for->list, false); GDScriptParser::DataType list_type = p_for->list->get_datatype(); + list_visible_type = list_type.to_string(); if (!list_type.is_hard_type()) { mark_node_unsafe(p_for->list); } @@ -2051,8 +2054,37 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); } } + if (p_for->variable) { - p_for->variable->set_datatype(variable_type); + if (p_for->datatype_specifier) { + GDScriptParser::DataType specified_type = type_from_metatype(resolve_datatype(p_for->datatype_specifier)); + if (!specified_type.is_variant()) { + if (variable_type.is_variant() || !variable_type.is_hard_type()) { + mark_node_unsafe(p_for->variable); + p_for->use_conversion_assign = true; + } else if (!is_type_compatible(specified_type, variable_type, true, p_for->variable)) { + if (is_type_compatible(variable_type, specified_type)) { + mark_node_unsafe(p_for->variable); + p_for->use_conversion_assign = true; + } else { + push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier); + } + } else if (!is_type_compatible(specified_type, variable_type)) { + p_for->use_conversion_assign = true; +#ifdef DEBUG_ENABLED + } else { + parser->push_warning(p_for->datatype_specifier, GDScriptWarning::REDUNDANT_FOR_VARIABLE_TYPE, p_for->variable->name, variable_type.to_string(), specified_type.to_string()); +#endif + } +#ifdef DEBUG_ENABLED + } else { + parser->push_warning(p_for->datatype_specifier, GDScriptWarning::REDUNDANT_FOR_VARIABLE_TYPE, p_for->variable->name, variable_type.to_string(), specified_type.to_string()); +#endif + } + p_for->variable->set_datatype(specified_type); + } else { + p_for->variable->set_datatype(variable_type); + } } resolve_suite(p_for->loop); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 6057a00f9b..af7862efc5 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1494,19 +1494,16 @@ void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_typ for_container_variables.push_back(container); } -void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable, const Address &p_list) { +void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) { const Address &container = for_container_variables.back()->get(); // Assign container. append_opcode(GDScriptFunction::OPCODE_ASSIGN); append(container); append(p_list); - - for_iterator_variables.push_back(p_variable); } -void GDScriptByteCodeGenerator::write_for() { - const Address &iterator = for_iterator_variables.back()->get(); +void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion) { const Address &counter = for_counter_variables.back()->get(); const Address &container = for_container_variables.back()->get(); @@ -1599,11 +1596,16 @@ void GDScriptByteCodeGenerator::write_for() { } } + Address temp; + if (p_use_conversion) { + temp = Address(Address::LOCAL_VARIABLE, add_local("@iterator_temp", GDScriptDataType())); + } + // Begin loop. append_opcode(begin_opcode); append(counter); append(container); - append(iterator); + append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // End of loop address, will be patched. append_opcode(GDScriptFunction::OPCODE_JUMP); @@ -1615,9 +1617,17 @@ void GDScriptByteCodeGenerator::write_for() { append_opcode(iterate_opcode); append(counter); append(container); - append(iterator); + append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // Jump destination, will be patched. + + if (p_use_conversion) { + write_assign_with_conversion(p_variable, temp); + const GDScriptDataType &type = p_variable.type; + if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { + write_assign_false(temp); // Can contain RefCounted, so clear it. + } + } } void GDScriptByteCodeGenerator::write_endfor() { @@ -1639,7 +1649,6 @@ void GDScriptByteCodeGenerator::write_endfor() { current_breaks_to_patch.pop_back(); // Pop state. - for_iterator_variables.pop_back(); for_counter_variables.pop_back(); for_container_variables.pop_back(); } diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index bbcd252b13..671dea5d6d 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -143,7 +143,6 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { // Lists since these can be nested. List<int> if_jmp_addrs; List<int> for_jmp_addrs; - List<Address> for_iterator_variables; List<Address> for_counter_variables; List<Address> for_container_variables; List<int> while_jmp_addrs; @@ -536,8 +535,8 @@ public: virtual void write_jump_if_shared(const Address &p_value) override; virtual void write_end_jump_if_shared() override; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override; - virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override; - virtual void write_for() override; + virtual void write_for_assignment(const Address &p_list) override; + virtual void write_for(const Address &p_variable, bool p_use_conversion) override; virtual void write_endfor() override; virtual void start_while_condition() override; virtual void write_while(const Address &p_condition) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 9810f5395a..cf17353dec 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -145,8 +145,8 @@ public: virtual void write_jump_if_shared(const Address &p_value) = 0; virtual void write_end_jump_if_shared() = 0; virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0; - virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0; - virtual void write_for() = 0; + virtual void write_for_assignment(const Address &p_list) = 0; + virtual void write_for(const Address &p_variable, bool p_use_conversion) = 0; virtual void write_endfor() = 0; virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation. virtual void write_while(const Address &p_condition) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 3366fa2eec..f964db231a 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1953,13 +1953,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - gen->write_for_assignment(iterator, list); + gen->write_for_assignment(list); if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } - gen->write_for(); + gen->write_for(iterator, for_n->use_conversion_assign); err = _parse_block(codegen, for_n->loop); if (err) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b76ceea11f..1dde67d2d1 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1850,7 +1850,18 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { n_for->variable = parse_identifier(); } - consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable name.)"); + if (match(GDScriptTokenizer::Token::COLON)) { + n_for->datatype_specifier = parse_type(); + if (n_for->datatype_specifier == nullptr) { + push_error(R"(Expected type specifier after ":".)"); + } + } + + if (n_for->datatype_specifier == nullptr) { + consume(GDScriptTokenizer::Token::IN, R"(Expected "in" or ":" after "for" variable name.)"); + } else { + consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable type specifier.)"); + } n_for->list = parse_expression(false); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 71660d8f60..652faaebc3 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -814,6 +814,8 @@ public: struct ForNode : public Node { IdentifierNode *variable = nullptr; + TypeNode *datatype_specifier = nullptr; + bool use_conversion_assign = false; ExpressionNode *list = nullptr; SuiteNode *loop = nullptr; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 24aa793c47..4fec445995 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -113,6 +113,14 @@ String GDScriptWarning::get_message() const { return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)"; case REDUNDANT_AWAIT: return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)"; + case REDUNDANT_FOR_VARIABLE_TYPE: + CHECK_SYMBOLS(3); + if (symbols[1] == symbols[2]) { + return vformat(R"(The for loop iterator "%s" already has inferred type "%s", the specified type is redundant.)", symbols[0], symbols[1]); + } else { + return vformat(R"(The for loop iterator "%s" has inferred type "%s" but its supertype "%s" is specified.)", symbols[0], symbols[1], symbols[2]); + } + break; case ASSERT_ALWAYS_TRUE: return "Assert statement is redundant because the expression is always true."; case ASSERT_ALWAYS_FALSE: @@ -209,6 +217,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "STATIC_CALLED_ON_INSTANCE", "REDUNDANT_STATIC_UNLOAD", "REDUNDANT_AWAIT", + "REDUNDANT_FOR_VARIABLE_TYPE", "ASSERT_ALWAYS_TRUE", "ASSERT_ALWAYS_FALSE", "INTEGER_DIVISION", diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 8444d46a88..73e12eb20e 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -73,6 +73,7 @@ public: STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. REDUNDANT_STATIC_UNLOAD, // The `@static_unload` annotation is used but the class does not have static data. REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine). + REDUNDANT_FOR_VARIABLE_TYPE, // The for variable type specifier is a supertype of the inferred type. ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false. INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded. @@ -120,6 +121,7 @@ public: WARN, // STATIC_CALLED_ON_INSTANCE WARN, // REDUNDANT_STATIC_UNLOAD WARN, // REDUNDANT_AWAIT + WARN, // REDUNDANT_FOR_VARIABLE_TYPE WARN, // ASSERT_ALWAYS_TRUE WARN, // ASSERT_ALWAYS_FALSE WARN, // INTEGER_DIVISION diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 062be0fe20..8c44483288 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -73,6 +73,7 @@ void GDScriptLanguageServer::_notification(int p_what) { } void GDScriptLanguageServer::thread_main(void *p_userdata) { + set_current_thread_safe_for_nodes(true); GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata); while (self->thread_running) { // Poll 20 times per second diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd new file mode 100644 index 0000000000..7e3b6e3c39 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Resource] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out new file mode 100644 index 0000000000..8f8a368f9a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_wrong_specified_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Unable to iterate on value of type "Array[Resource]" with variable of type "Node". diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd new file mode 100644 index 0000000000..1b32491e48 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Node] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out new file mode 100644 index 0000000000..3b3fbd9bd1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_equal_to_inferred.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> REDUNDANT_FOR_VARIABLE_TYPE +>> The for loop iterator "node" already has inferred type "Node", the specified type is redundant. diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd new file mode 100644 index 0000000000..fcbc13c53d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array[Node2D] = [] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out new file mode 100644 index 0000000000..36d4a161d3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/warnings/for_loop_specified_type_is_supertype_of_inferred.out @@ -0,0 +1,5 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> REDUNDANT_FOR_VARIABLE_TYPE +>> The for loop iterator "node" has inferred type "Node2D" but its supertype "Node" is specified. diff --git a/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd new file mode 100644 index 0000000000..cdc278ecf5 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.gd @@ -0,0 +1,4 @@ +func test(): + var a: Array = [Resource.new()] + for node: Node in a: + print(node) diff --git a/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out new file mode 100644 index 0000000000..b7ef07afb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/for_loop_iterator_type_not_match_specified.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/for_loop_iterator_type_not_match_specified.gd +>> 3 +>> Trying to assign value of type 'Resource' to a variable of type 'Node'. diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd new file mode 100644 index 0000000000..58b4df5a79 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.gd @@ -0,0 +1,34 @@ +func test(): + print("Test range.") + for e: float in range(2, 5): + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test int.") + for e: float in 3: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test untyped int array.") + var a1 := [10, 20, 30] + for e: float in a1: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test typed int array.") + var a2: Array[int] = [10, 20, 30] + for e: float in a2: + var elem := e + prints(var_to_str(e), var_to_str(elem)) + + print("Test String-keys dictionary.") + var d1 := {a = 1, b = 2, c = 3} + for k: StringName in d1: + var key := k + prints(var_to_str(k), var_to_str(key)) + + print("Test RefCounted-keys dictionary.") + var d2 := {RefCounted.new(): 1, Resource.new(): 2, ConfigFile.new(): 3} + for k: RefCounted in d2: + var key := k + prints(k.get_class(), key.get_class()) diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out new file mode 100644 index 0000000000..f67f7d89d5 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_specified_types.out @@ -0,0 +1,25 @@ +GDTEST_OK +Test range. +2.0 2.0 +3.0 3.0 +4.0 4.0 +Test int. +0.0 0.0 +1.0 1.0 +2.0 2.0 +Test untyped int array. +10.0 10.0 +20.0 20.0 +30.0 30.0 +Test typed int array. +10.0 10.0 +20.0 20.0 +30.0 30.0 +Test String-keys dictionary. +&"a" &"a" +&"b" &"b" +&"c" &"c" +Test RefCounted-keys dictionary. +RefCounted RefCounted +Resource Resource +ConfigFile ConfigFile diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 8c5782dc41..70c72dab07 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -30,7 +30,10 @@ #include "gpu_particles_2d.h" +#include "scene/2d/cpu_particles_2d.h" #include "scene/resources/atlas_texture.h" +#include "scene/resources/curve_texture.h" +#include "scene/resources/gradient_texture.h" #include "scene/resources/particle_process_material.h" #include "scene/scene_string_names.h" @@ -435,6 +438,97 @@ void GPUParticles2D::restart() { } } +void GPUParticles2D::convert_from_particles(Node *p_particles) { + CPUParticles2D *cpu_particles = Object::cast_to<CPUParticles2D>(p_particles); + ERR_FAIL_NULL_MSG(cpu_particles, "Only CPUParticles2D nodes can be converted to GPUParticles2D."); + + set_emitting(cpu_particles->is_emitting()); + set_amount(cpu_particles->get_amount()); + set_lifetime(cpu_particles->get_lifetime()); + set_one_shot(cpu_particles->get_one_shot()); + set_pre_process_time(cpu_particles->get_pre_process_time()); + set_explosiveness_ratio(cpu_particles->get_explosiveness_ratio()); + set_randomness_ratio(cpu_particles->get_randomness_ratio()); + set_use_local_coordinates(cpu_particles->get_use_local_coordinates()); + set_fixed_fps(cpu_particles->get_fixed_fps()); + set_fractional_delta(cpu_particles->get_fractional_delta()); + set_speed_scale(cpu_particles->get_speed_scale()); + set_draw_order(DrawOrder(cpu_particles->get_draw_order())); + set_texture(cpu_particles->get_texture()); + + Ref<Material> mat = cpu_particles->get_material(); + if (mat.is_valid()) { + set_material(mat); + } + + Ref<ParticleProcessMaterial> proc_mat = memnew(ParticleProcessMaterial); + set_process_material(proc_mat); + Vector2 dir = cpu_particles->get_direction(); + proc_mat->set_direction(Vector3(dir.x, dir.y, 0)); + proc_mat->set_spread(cpu_particles->get_spread()); + proc_mat->set_color(cpu_particles->get_color()); + + Ref<Gradient> color_grad = cpu_particles->get_color_ramp(); + if (color_grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(color_grad); + proc_mat->set_color_ramp(tex); + } + + Ref<Gradient> color_init_grad = cpu_particles->get_color_initial_ramp(); + if (color_init_grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(color_init_grad); + proc_mat->set_color_initial_ramp(tex); + } + + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, cpu_particles->get_particle_flag(CPUParticles2D::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + + proc_mat->set_emission_shape(ParticleProcessMaterial::EmissionShape(cpu_particles->get_emission_shape())); + proc_mat->set_emission_sphere_radius(cpu_particles->get_emission_sphere_radius()); + + Vector2 rect_extents = cpu_particles->get_emission_rect_extents(); + proc_mat->set_emission_box_extents(Vector3(rect_extents.x, rect_extents.y, 0)); + + if (cpu_particles->get_split_scale()) { + Ref<CurveXYZTexture> scale3D = memnew(CurveXYZTexture); + scale3D->set_curve_x(cpu_particles->get_scale_curve_x()); + scale3D->set_curve_y(cpu_particles->get_scale_curve_y()); + proc_mat->set_param_texture(ParticleProcessMaterial::PARAM_SCALE, scale3D); + } + + Vector2 gravity = cpu_particles->get_gravity(); + proc_mat->set_gravity(Vector3(gravity.x, gravity.y, 0)); + proc_mat->set_lifetime_randomness(cpu_particles->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + proc_mat->set_param_min(ParticleProcessMaterial::m_param, cpu_particles->get_param_min(CPUParticles2D::m_param)); \ + { \ + Ref<Curve> curve = cpu_particles->get_param_curve(CPUParticles2D::m_param); \ + if (curve.is_valid()) { \ + Ref<CurveTexture> tex = memnew(CurveTexture); \ + tex->set_curve(curve); \ + proc_mat->set_param_texture(ParticleProcessMaterial::m_param, tex); \ + } \ + } \ + proc_mat->set_param_max(ParticleProcessMaterial::m_param, cpu_particles->get_param_max(CPUParticles2D::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void GPUParticles2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { @@ -680,6 +774,8 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_trail_section_subdivisions", "subdivisions"), &GPUParticles2D::set_trail_section_subdivisions); ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &GPUParticles2D::convert_from_particles); + ADD_SIGNAL(MethodInfo("finished")); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 3131698e5c..97690b07fa 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -169,6 +169,8 @@ public: void restart(); Rect2 capture_rect() const; + void convert_from_particles(Node *p_particles); + GPUParticles2D(); ~GPUParticles2D(); }; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 3a23cbcff1..a7667267a6 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -30,6 +30,9 @@ #include "gpu_particles_3d.h" +#include "scene/3d/cpu_particles_3d.h" +#include "scene/resources/curve_texture.h" +#include "scene/resources/gradient_texture.h" #include "scene/resources/particle_process_material.h" #include "scene/scene_string_names.h" @@ -546,10 +549,98 @@ void GPUParticles3D::set_transform_align(TransformAlign p_align) { transform_align = p_align; RS::get_singleton()->particles_set_transform_align(particles, RS::ParticlesTransformAlign(transform_align)); } + GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const { return transform_align; } +void GPUParticles3D::convert_from_particles(Node *p_particles) { + CPUParticles3D *cpu_particles = Object::cast_to<CPUParticles3D>(p_particles); + ERR_FAIL_NULL_MSG(cpu_particles, "Only CPUParticles3D nodes can be converted to GPUParticles3D."); + + set_emitting(cpu_particles->is_emitting()); + set_amount(cpu_particles->get_amount()); + set_lifetime(cpu_particles->get_lifetime()); + set_one_shot(cpu_particles->get_one_shot()); + set_pre_process_time(cpu_particles->get_pre_process_time()); + set_explosiveness_ratio(cpu_particles->get_explosiveness_ratio()); + set_randomness_ratio(cpu_particles->get_randomness_ratio()); + set_use_local_coordinates(cpu_particles->get_use_local_coordinates()); + set_fixed_fps(cpu_particles->get_fixed_fps()); + set_fractional_delta(cpu_particles->get_fractional_delta()); + set_speed_scale(cpu_particles->get_speed_scale()); + set_draw_order(DrawOrder(cpu_particles->get_draw_order())); + set_draw_pass_mesh(0, cpu_particles->get_mesh()); + + Ref<ParticleProcessMaterial> proc_mat = memnew(ParticleProcessMaterial); + set_process_material(proc_mat); + + proc_mat->set_direction(cpu_particles->get_direction()); + proc_mat->set_spread(cpu_particles->get_spread()); + proc_mat->set_flatness(cpu_particles->get_flatness()); + proc_mat->set_color(cpu_particles->get_color()); + + Ref<Gradient> grad = cpu_particles->get_color_ramp(); + if (grad.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(grad); + proc_mat->set_color_ramp(tex); + } + + Ref<Gradient> grad_init = cpu_particles->get_color_initial_ramp(); + if (grad_init.is_valid()) { + Ref<GradientTexture1D> tex = memnew(GradientTexture1D); + tex->set_gradient(grad_init); + proc_mat->set_color_initial_ramp(tex); + } + + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_ROTATE_Y, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_ROTATE_Y)); + proc_mat->set_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z, cpu_particles->get_particle_flag(CPUParticles3D::PARTICLE_FLAG_DISABLE_Z)); + + proc_mat->set_emission_shape(ParticleProcessMaterial::EmissionShape(cpu_particles->get_emission_shape())); + proc_mat->set_emission_sphere_radius(cpu_particles->get_emission_sphere_radius()); + proc_mat->set_emission_box_extents(cpu_particles->get_emission_box_extents()); + + if (cpu_particles->get_split_scale()) { + Ref<CurveXYZTexture> scale3D = memnew(CurveXYZTexture); + scale3D->set_curve_x(cpu_particles->get_scale_curve_x()); + scale3D->set_curve_y(cpu_particles->get_scale_curve_y()); + scale3D->set_curve_z(cpu_particles->get_scale_curve_z()); + proc_mat->set_param_texture(ParticleProcessMaterial::PARAM_SCALE, scale3D); + } + + proc_mat->set_gravity(cpu_particles->get_gravity()); + proc_mat->set_lifetime_randomness(cpu_particles->get_lifetime_randomness()); + +#define CONVERT_PARAM(m_param) \ + proc_mat->set_param_min(ParticleProcessMaterial::m_param, cpu_particles->get_param_min(CPUParticles3D::m_param)); \ + { \ + Ref<Curve> curve = cpu_particles->get_param_curve(CPUParticles3D::m_param); \ + if (curve.is_valid()) { \ + Ref<CurveTexture> tex = memnew(CurveTexture); \ + tex->set_curve(curve); \ + proc_mat->set_param_texture(ParticleProcessMaterial::m_param, tex); \ + } \ + } \ + proc_mat->set_param_max(ParticleProcessMaterial::m_param, cpu_particles->get_param_max(CPUParticles3D::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles3D::set_amount); @@ -613,6 +704,8 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &GPUParticles3D::convert_from_particles); + ADD_SIGNAL(MethodInfo("finished")); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index dba6a8f2ab..6e9083bda2 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -178,6 +178,8 @@ public: void emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags); AABB capture_aabb() const; + void convert_from_particles(Node *p_particles); + GPUParticles3D(); ~GPUParticles3D(); }; diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index d2a1a5ab9c..64950c6991 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -285,7 +285,7 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID cluster_render_buffer = RD::get_singleton()->storage_buffer_create(cluster_render_buffer_size); cluster_buffer = RD::get_singleton()->storage_buffer_create(cluster_buffer_size); - render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData *) * render_element_max)); + render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData) * render_element_max)); render_element_count = 0; element_buffer = RD::get_singleton()->storage_buffer_create(sizeof(RenderElementData) * render_element_max); |