diff options
57 files changed, 714 insertions, 244 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 690e74855f..0b4abac1af 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -52,5 +52,8 @@ body:    attributes:      label: Minimal reproduction project      description: | -      A small Godot project which reproduces the issue. Highly recommended to speed up troubleshooting. -      Drag and drop a ZIP archive to upload it. Be sure to not include the ".godot" folder in the archive. +      A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`). +      Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field. +      Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.** +  validations: +    required: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1e8d3a59d..63e6a89e64 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,6 +63,8 @@ understand that:  - What happens to you may not happen to other users.  - We can't take the time to look at your project, understand how it is set up    and then figure out why it's failing. +- On the contributors' end, recreating a test project from scratch takes valuable +  time that can be saved by uploading a *minimal* project.  To speed up our work, **please upload a minimal project** that isolates  and reproduces the issue. This is always the **best way for us to fix it**. @@ -74,7 +76,7 @@ if your ZIP file isn't accepted by GitHub because it's too large.  We recommend always attaching a minimal reproduction project, even if the issue  may seem simple to reproduce manually. -**Note for C# users:** If your issue is not .NET-specific, please upload a +**Note for C# users:** If your issue is *not* .NET-specific, please upload a  minimal reproduction project written in GDScript.  This will make it easier for contributors to reproduce the issue  locally as not everyone has a .NET setup available. diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index aa1b323db2..0118b4c6af 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -107,6 +107,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &  }  Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) { +	ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); +  	Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);  	if (f.is_null()) {  		return ERR_FILE_CANT_OPEN; diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index ab948dd0de..b9c8ab0f6c 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -25,10 +25,12 @@  		m.mesh = arr_mesh  		[/gdscript]  		[csharp] -		var vertices = new Godot.Collections.Array<Vector3>(); -		vertices.Add(new Vector3(0, 1, 0)); -		vertices.Add(new Vector3(1, 0, 0)); -		vertices.Add(new Vector3(0, 0, 1)); +		var vertices = new Vector3[] +		{ +		    new Vector3(0, 1, 0), +		    new Vector3(1, 0, 0), +		    new Vector3(0, 0, 1), +		};  		// Initialize the ArrayMesh.  		var arrMesh = new ArrayMesh(); @@ -38,7 +40,7 @@  		// Create the Mesh.  		arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays); -		var m = new MeshInstance(); +		var m = new MeshInstance3D();  		m.Mesh = arrMesh;  		[/csharp]  		[/codeblocks] diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 2159001a30..8a38deeebe 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1010,6 +1010,10 @@  		<member name="layout_direction" type="int" setter="set_layout_direction" getter="get_layout_direction" enum="Control.LayoutDirection" default="0">  			Controls layout direction and text writing direction. Right-to-left layouts are necessary for certain languages (e.g. Arabic and Hebrew).  		</member> +		<member name="localize_numeral_system" type="bool" setter="set_localize_numeral_system" getter="is_localizing_numeral_system" default="true"> +			If [code]true[/code], automatically converts code line numbers, list indices, [SpinBox] and [ProgressBar] values from the Western Arabic (0..9) to the numeral systems used in current locale. +			[b]Note:[/b] Numbers within the text are not automatically converted, it can be done manually, using [method TextServer.format_number]. +		</member>  		<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" enum="Control.CursorShape" default="0">  			The default cursor shape for this control. Useful for Godot plugins and applications or games that use the system's mouse cursors.  			[b]Note:[/b] On Linux, shapes may vary depending on the cursor theme of the system. diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 492a438d9b..4f41baebf7 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -29,6 +29,10 @@ MARKUP_ALLOWED_SUBSEQUENT = " -.,:;!?\\/'\")]}>"  # write in this script (check `translate()` uses), and also hardcoded in  # `doc/translations/extract.py` to include them in the source POT file.  BASE_STRINGS = [ +    "Objects", +    "Nodes", +    "Resources", +    "Globals",      "Description",      "Tutorials",      "Properties", @@ -63,6 +67,13 @@ strings_l10n: Dict[str, str] = {}  STYLES: Dict[str, str] = {} +CLASS_GROUPS: Dict[str, str] = { +    "global": "Globals", +    "node": "Nodes", +    "resource": "Resources", +    "class": "Objects", +} +  class State:      def __init__(self) -> None: @@ -329,7 +340,7 @@ class State:          return cast      def sort_classes(self) -> None: -        self.classes = OrderedDict(sorted(self.classes.items(), key=lambda t: t[0])) +        self.classes = OrderedDict(sorted(self.classes.items(), key=lambda t: t[0].lower()))  class TypeName: @@ -601,12 +612,25 @@ def main() -> None:      print("Generating the RST class reference...") +    grouped_classes: Dict[str, List[str]] = {} +      for class_name, class_def in state.classes.items():          if args.filter and not pattern.search(class_def.filepath):              continue          state.current_class = class_name          make_rst_class(class_def, state, args.dry_run, args.output) +        group_name = get_class_group(class_def, state) + +        if group_name not in grouped_classes: +            grouped_classes[group_name] = [] +        grouped_classes[group_name].append(class_name) + +    print("") +    print("Generating the index file...") + +    make_rst_index(grouped_classes, args.dry_run, args.output) +      print("")      if state.num_warnings >= 2: @@ -655,6 +679,39 @@ def translate(string: str) -> str:      return strings_l10n.get(string, string) +def get_git_branch() -> str: +    if hasattr(version, "docs") and version.docs != "latest": +        return version.docs + +    return "master" + + +def get_class_group(class_def: ClassDef, state: State) -> str: +    group_name = "class" +    class_name = class_def.name + +    if class_name.startswith("@"): +        group_name = "global" +    elif class_def.inherits: +        inherits = class_def.inherits.strip() + +        while inherits in state.classes: +            if inherits == "Node": +                group_name = "node" +                break +            if inherits == "Resource": +                group_name = "resource" +                break + +            inode = state.classes[inherits].inherits +            if inode: +                inherits = inode.strip() +            else: +                break + +    return group_name + +  # Generator methods. @@ -672,10 +729,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:      # Warn contributors not to edit this file directly.      # Also provide links to the source files for reference. -    git_branch = "master" -    if hasattr(version, "docs") and version.docs != "latest": -        git_branch = version.docs - +    git_branch = get_git_branch()      source_xml_path = os.path.relpath(class_def.filepath, root_directory).replace("\\", "/")      source_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/{source_xml_path}"      generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" @@ -723,15 +777,30 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:              f.write(make_type(child, state))          f.write("\n\n") +    has_description = False +      # Brief description -    if class_def.brief_description is not None: +    if class_def.brief_description is not None and class_def.brief_description.strip() != "": +        has_description = True +          f.write(f"{format_text_block(class_def.brief_description.strip(), class_def, state)}\n\n")      # Class description      if class_def.description is not None and class_def.description.strip() != "": +        has_description = True +          f.write(make_heading("Description", "-"))          f.write(f"{format_text_block(class_def.description.strip(), class_def, state)}\n\n") +    if not has_description: +        f.write(".. container:: contribute\n\n\t") +        f.write( +            translate( +                "There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +            ) +            + "\n\n" +        ) +      # Online tutorials      if len(class_def.tutorials) > 0:          f.write(make_heading("Tutorials", "-")) @@ -872,6 +941,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:                  if m.description is not None and m.description.strip() != "":                      f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") +                else: +                    f.write(".. container:: contribute\n\n\t") +                    f.write( +                        translate( +                            "There is currently no description for this annotation. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                        ) +                        + "\n\n" +                    )                  index += 1 @@ -904,6 +981,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:              if property_def.text is not None and property_def.text.strip() != "":                  f.write(f"{format_text_block(property_def.text.strip(), property_def, state)}\n\n") +            else: +                f.write(".. container:: contribute\n\n\t") +                f.write( +                    translate( +                        "There is currently no description for this property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                    ) +                    + "\n\n" +                )              index += 1 @@ -925,6 +1010,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:                  if m.description is not None and m.description.strip() != "":                      f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") +                else: +                    f.write(".. container:: contribute\n\n\t") +                    f.write( +                        translate( +                            "There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                        ) +                        + "\n\n" +                    )                  index += 1 @@ -945,6 +1038,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:                  if m.description is not None and m.description.strip() != "":                      f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") +                else: +                    f.write(".. container:: contribute\n\n\t") +                    f.write( +                        translate( +                            "There is currently no description for this method. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                        ) +                        + "\n\n" +                    )                  index += 1 @@ -967,6 +1068,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:                  if m.description is not None and m.description.strip() != "":                      f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") +                else: +                    f.write(".. container:: contribute\n\n\t") +                    f.write( +                        translate( +                            "There is currently no description for this operator. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                        ) +                        + "\n\n" +                    )                  index += 1 @@ -992,6 +1101,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:              if theme_item_def.text is not None and theme_item_def.text.strip() != "":                  f.write(f"{format_text_block(theme_item_def.text.strip(), theme_item_def, state)}\n\n") +            else: +                f.write(".. container:: contribute\n\n\t") +                f.write( +                    translate( +                        "There is currently no description for this theme property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" +                    ) +                    + "\n\n" +                )              index += 1 @@ -1142,6 +1259,46 @@ def make_link(url: str, title: str) -> str:      return f"`{url} <{url}>`__" +def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_dir: str) -> None: + +    if dry_run: +        f = open(os.devnull, "w", encoding="utf-8") +    else: +        f = open(os.path.join(output_dir, "index.rst"), "w", encoding="utf-8") + +    # Remove the "Edit on Github" button from the online docs page. +    f.write(":github_url: hide\n\n") + +    # Warn contributors not to edit this file directly. +    # Also provide links to the source files for reference. + +    git_branch = get_git_branch() +    generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" + +    f.write(".. DO NOT EDIT THIS FILE!!!\n") +    f.write(".. Generated automatically from Godot engine sources.\n") +    f.write(f".. Generator: {generator_github_url}.\n\n") + +    f.write(".. _doc_class_reference:\n\n") + +    for group_name in CLASS_GROUPS: +        if group_name in grouped_classes: +            group_title = translate(CLASS_GROUPS[group_name]) + +            f.write(f"{group_title}\n") +            f.write(f"{'=' * len(group_title)}\n\n") + +            f.write(".. toctree::\n") +            f.write("    :maxdepth: 1\n") +            f.write("    :name: toc-class-ref-globals\n") +            f.write("\n") + +            for class_name in grouped_classes[group_name]: +                f.write(f"    class_{class_name.lower()}\n") + +            f.write("\n") + +  # Formatting helpers. diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 767ac2bf1a..15743c2d78 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -251,6 +251,8 @@ void TextureStorage::canvas_texture_free(RID p_rid) {  void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) {  	CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); +	ERR_FAIL_NULL(ct); +  	switch (p_channel) {  		case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: {  			ct->diffuse = p_texture; @@ -266,6 +268,8 @@ void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::Canvas  void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) {  	CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); +	ERR_FAIL_NULL(ct); +  	ct->specular_color.r = p_specular_color.r;  	ct->specular_color.g = p_specular_color.g;  	ct->specular_color.b = p_specular_color.b; @@ -274,11 +278,15 @@ void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture,  void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) {  	CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); +	ERR_FAIL_NULL(ct); +  	ct->texture_filter = p_filter;  }  void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) {  	CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); +	ERR_FAIL_NULL(ct); +  	ct->texture_repeat = p_repeat;  } diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index c7c5fadbeb..6e61006395 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3826,7 +3826,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF  			vrs_reference.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;  			vrs_reference.aspectMask = VK_IMAGE_ASPECT_NONE; -			Size2i texel_size = context->get_vrs_capabilities().max_texel_size; +			Size2i texel_size = context->get_vrs_capabilities().texel_size;  			VkFragmentShadingRateAttachmentInfoKHR &vrs_attachment_info = vrs_attachment_info_array[i];  			vrs_attachment_info.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR; @@ -9724,6 +9724,12 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const {  			VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities();  			return subgroup_capabilities.supported_operations_flags_rd();  		} +		case LIMIT_VRS_TEXEL_WIDTH: { +			return context->get_vrs_capabilities().texel_size.x; +		} +		case LIMIT_VRS_TEXEL_HEIGHT: { +			return context->get_vrs_capabilities().texel_size.y; +		}  		default:  			ERR_FAIL_V(0);  	} diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 464ab474e1..381df6d65e 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -625,6 +625,9 @@ Error VulkanContext::_check_capabilities() {  	vrs_capabilities.pipeline_vrs_supported = false;  	vrs_capabilities.primitive_vrs_supported = false;  	vrs_capabilities.attachment_vrs_supported = false; +	vrs_capabilities.min_texel_size = Size2i(); +	vrs_capabilities.max_texel_size = Size2i(); +	vrs_capabilities.texel_size = Size2i();  	multiview_capabilities.is_supported = false;  	multiview_capabilities.geometry_shader_is_supported = false;  	multiview_capabilities.tessellation_shader_is_supported = false; @@ -788,6 +791,10 @@ Error VulkanContext::_check_capabilities() {  				vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width;  				vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; +				// We'll attempt to default to a texel size of 16x16 +				vrs_capabilities.texel_size.x = CLAMP(16, vrs_capabilities.min_texel_size.x, vrs_capabilities.max_texel_size.x); +				vrs_capabilities.texel_size.y = CLAMP(16, vrs_capabilities.min_texel_size.y, vrs_capabilities.max_texel_size.y); +  				print_verbose(String("  Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")"));  			} diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index d6a25c5cd7..8cf33fa463 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -76,6 +76,8 @@ public:  		Size2i min_texel_size;  		Size2i max_texel_size; + +		Size2i texel_size; // The texel size we'll use  	};  	struct ShaderCapabilities { diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 0c8176a44b..9b9b176e82 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1678,6 +1678,7 @@ void AnimationTimelineEdit::_notification(int p_what) {  			}  			draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); +			update_values();  		} break;  	}  } @@ -1700,7 +1701,6 @@ void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation, boo  		play_position->hide();  	}  	queue_redraw(); -	update_values();  }  Size2 AnimationTimelineEdit::get_minimum_size() const { @@ -1749,6 +1749,7 @@ void AnimationTimelineEdit::update_values() {  		length->set_step(1);  		length->set_tooltip_text(TTR("Animation length (frames)"));  		time_icon->set_tooltip_text(TTR("Animation length (frames)")); +		track_edit->editor->_update_key_edit();  	} else {  		length->set_value(animation->get_length());  		length->set_step(0.001); @@ -1893,7 +1894,6 @@ void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origi  void AnimationTimelineEdit::set_use_fps(bool p_use_fps) {  	use_fps = p_use_fps; -	update_values();  	queue_redraw();  } @@ -4793,6 +4793,7 @@ void AnimationTrackEditor::_update_step(double p_new_step) {  		if (step_value != 0.0) {  			step_value = 1.0 / step_value;  		} +		timeline->queue_redraw();  	}  	undo_redo->add_do_method(animation.ptr(), "set_step", step_value);  	undo_redo->add_undo_method(animation.ptr(), "set_step", animation->get_step()); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 5c51921d93..01ff943409 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -135,6 +135,7 @@ class AnimationTrackEditor;  class AnimationTrackEdit : public Control {  	GDCLASS(AnimationTrackEdit, Control); +	friend class AnimationTimelineEdit;  	enum {  		MENU_CALL_MODE_CONTINUOUS, @@ -293,6 +294,7 @@ public:  class AnimationTrackEditor : public VBoxContainer {  	GDCLASS(AnimationTrackEditor, VBoxContainer); +	friend class AnimationTimelineEdit;  	Ref<Animation> animation;  	bool read_only = false; diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 510dc345bf..1e734bb97f 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -377,10 +377,12 @@ void FindReplaceBar::_update_results_count() {  			if (is_whole_words()) {  				if (col_pos > 0 && !is_symbol(line_text[col_pos - 1])) { -					break; +					col_pos += searched.length(); +					continue;  				} -				if (col_pos + line_text.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) { -					break; +				if (col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) { +					col_pos += searched.length(); +					continue;  				}  			} diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index efa85dadee..0a443ee645 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -446,7 +446,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {  	return OK;  } -void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons) { +void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods) {  	Ref<Font> font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));  	class_desc->pop(); // title font size  	class_desc->pop(); // title font @@ -496,10 +496,6 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods,  				class_desc->pop(); //cell  			} -			if (!m[i].description.strip_edges().is_empty() || m[i].errors_returned.size() > 0) { -				r_method_descrpitons = true; -			} -  			_add_method(m[i], true);  		} @@ -717,11 +713,15 @@ void EditorHelp::_update_doc() {  		class_desc->add_newline();  	} +	bool has_description = false; +  	class_desc->add_newline();  	class_desc->add_newline();  	// Brief description  	if (!cd.brief_description.strip_edges().is_empty()) { +		has_description = true; +  		class_desc->push_color(text_color);  		class_desc->push_font(doc_bold_font);  		class_desc->push_indent(1); @@ -736,6 +736,8 @@ void EditorHelp::_update_doc() {  	// Class description  	if (!cd.description.strip_edges().is_empty()) { +		has_description = true; +  		section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_paragraph_count() - 2));  		description_line = class_desc->get_paragraph_count() - 2;  		class_desc->push_color(title_color); @@ -760,6 +762,22 @@ void EditorHelp::_update_doc() {  		class_desc->add_newline();  	} +	if (!has_description) { +		class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); +		class_desc->add_text(" "); +		class_desc->push_color(comment_color); + +		if (cd.is_script_doc) { +			class_desc->append_text(TTR("There is currently no description for this class.")); +		} else { +			class_desc->append_text(TTR("There is currently no description for this class. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text)); +		} + +		class_desc->pop(); +		class_desc->add_newline(); +		class_desc->add_newline(); +	} +  	// Online tutorials  	if (cd.tutorials.size()) {  		class_desc->push_color(title_color); @@ -796,7 +814,6 @@ void EditorHelp::_update_doc() {  	// Properties overview  	HashSet<String> skip_methods; -	bool property_descr = false;  	bool has_properties = cd.properties.size() != 0;  	if (cd.is_script_doc) { @@ -874,7 +891,6 @@ void EditorHelp::_update_doc() {  			if (describe) {  				class_desc->pop(); -				property_descr = true;  			}  			class_desc->pop(); @@ -959,9 +975,6 @@ void EditorHelp::_update_doc() {  	}  	// Methods overview -	bool constructor_descriptions = false; -	bool method_descriptions = false; -	bool operator_descriptions = false;  	bool sort_methods = EDITOR_GET("text_editor/help/sort_functions_alphabetically");  	Vector<DocData::MethodDoc> methods; @@ -989,19 +1002,20 @@ void EditorHelp::_update_doc() {  		class_desc->push_font(doc_title_font);  		class_desc->push_font_size(doc_title_font_size);  		class_desc->add_text(TTR("Constructors")); -		_update_method_list(cd.constructors, constructor_descriptions); +		_update_method_list(cd.constructors);  	}  	if (!methods.is_empty()) {  		if (sort_methods) {  			methods.sort();  		} +  		section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_paragraph_count() - 2));  		class_desc->push_color(title_color);  		class_desc->push_font(doc_title_font);  		class_desc->push_font_size(doc_title_font_size);  		class_desc->add_text(TTR("Methods")); -		_update_method_list(methods, method_descriptions); +		_update_method_list(methods);  	}  	if (!cd.operators.is_empty()) { @@ -1014,7 +1028,7 @@ void EditorHelp::_update_doc() {  		class_desc->push_font(doc_title_font);  		class_desc->push_font_size(doc_title_font_size);  		class_desc->add_text(TTR("Operators")); -		_update_method_list(cd.operators, operator_descriptions); +		_update_method_list(cd.operators);  	}  	// Theme properties @@ -1507,7 +1521,7 @@ void EditorHelp::_update_doc() {  	}  	// Property descriptions -	if (property_descr) { +	if (has_properties) {  		section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2));  		class_desc->push_color(title_color);  		class_desc->push_font(doc_title_font); @@ -1682,7 +1696,7 @@ void EditorHelp::_update_doc() {  	}  	// Constructor descriptions -	if (constructor_descriptions) { +	if (!cd.constructors.is_empty()) {  		section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_paragraph_count() - 2));  		class_desc->push_color(title_color);  		class_desc->push_font(doc_title_font); @@ -1692,7 +1706,7 @@ void EditorHelp::_update_doc() {  	}  	// Method descriptions -	if (method_descriptions) { +	if (!methods.is_empty()) {  		section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_paragraph_count() - 2));  		class_desc->push_color(title_color);  		class_desc->push_font(doc_title_font); @@ -1702,7 +1716,7 @@ void EditorHelp::_update_doc() {  	}  	// Operator descriptions -	if (operator_descriptions) { +	if (!cd.operators.is_empty()) {  		section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_paragraph_count() - 2));  		class_desc->push_color(title_color);  		class_desc->push_font(doc_title_font); @@ -1710,6 +1724,8 @@ void EditorHelp::_update_doc() {  		class_desc->add_text(TTR("Operator Descriptions"));  		_update_method_descriptions(cd, cd.operators, "operator");  	} + +	// Free the scroll.  	scroll_locked = false;  } diff --git a/editor/editor_help.h b/editor/editor_help.h index c9c1afb51b..15bfdc7c91 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -167,7 +167,7 @@ class EditorHelp : public VBoxContainer {  	Error _goto_desc(const String &p_class, int p_vscr = -1);  	//void _update_history_buttons(); -	void _update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons); +	void _update_method_list(const Vector<DocData::MethodDoc> p_methods);  	void _update_method_descriptions(const DocData::ClassDoc p_classdoc, const Vector<DocData::MethodDoc> p_methods, const String &p_method_type);  	void _update_doc(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 736e7d8bf5..8df5808b11 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2994,7 +2994,7 @@ void FileSystemDock::_file_sort_popup(int p_id) {  MenuButton *FileSystemDock::_create_file_menu_button() {  	MenuButton *button = memnew(MenuButton);  	button->set_flat(true); -	button->set_tooltip_text(TTR("Sort files")); +	button->set_tooltip_text(TTR("Sort Files"));  	PopupMenu *p = button->get_popup();  	p->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_sort_popup)); @@ -3057,14 +3057,14 @@ FileSystemDock::FileSystemDock() {  	button_hist_prev->set_flat(true);  	button_hist_prev->set_disabled(true);  	button_hist_prev->set_focus_mode(FOCUS_NONE); -	button_hist_prev->set_tooltip_text(TTR("Previous Folder/File")); +	button_hist_prev->set_tooltip_text(TTR("Go to previous selected folder/file."));  	toolbar_hbc->add_child(button_hist_prev);  	button_hist_next = memnew(Button);  	button_hist_next->set_flat(true);  	button_hist_next->set_disabled(true);  	button_hist_next->set_focus_mode(FOCUS_NONE); -	button_hist_next->set_tooltip_text(TTR("Next Folder/File")); +	button_hist_next->set_tooltip_text(TTR("Go to next selected folder/file."));  	toolbar_hbc->add_child(button_hist_next);  	current_path = memnew(LineEdit); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 4ad33f3a97..8cb0e4e8d7 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -670,14 +670,14 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {  	backward_button = memnew(Button);  	backward_button->set_flat(true);  	general_options_hb->add_child(backward_button); -	backward_button->set_tooltip_text(TTR("Go to the previous edited object in history.")); +	backward_button->set_tooltip_text(TTR("Go to previous edited object in history."));  	backward_button->set_disabled(true);  	backward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_back));  	forward_button = memnew(Button);  	forward_button->set_flat(true);  	general_options_hb->add_child(forward_button); -	forward_button->set_tooltip_text(TTR("Go to the next edited object in history.")); +	forward_button->set_tooltip_text(TTR("Go to next edited object in history."));  	forward_button->set_disabled(true);  	forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward)); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index a7d90856ac..ce176e3a99 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -1138,6 +1138,7 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2  void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p_property, String p_label, Variant p_default_value) {  	ERR_FAIL_COND_MSG(!property.is_empty(), "Cannot setup TileDataDefaultEditor twice");  	property = p_property; +	property_type = p_type;  	// Update everything.  	if (property_editor) { @@ -1182,6 +1183,10 @@ void TileDataDefaultEditor::_notification(int p_what) {  	}  } +Variant::Type TileDataDefaultEditor::get_property_type() { +	return property_type; +} +  TileDataDefaultEditor::TileDataDefaultEditor() {  	undo_redo = EditorNode::get_undo_redo(); diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index c1560138b2..0a947fce8b 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -220,6 +220,7 @@ protected:  	StringName type;  	String property; +	Variant::Type property_type;  	void _notification(int p_what);  	virtual Variant _get_painted_value(); @@ -237,6 +238,7 @@ public:  	virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override;  	void setup_property_editor(Variant::Type p_type, String p_property, String p_label = "", Variant p_default_value = Variant()); +	Variant::Type get_property_type();  	TileDataDefaultEditor();  	~TileDataDefaultEditor(); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 57416ff55f..93f9df4d6e 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2267,6 +2267,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {  	patterns_help_label = memnew(Label);  	patterns_help_label->set_text(TTR("Drag and drop or paste a TileMap selection here to store a pattern.")); +	patterns_help_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);  	patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);  	patterns_item_list->add_child(patterns_help_label); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index ab54a093f2..8e69abd7ff 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -737,18 +737,29 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {  	// --- Custom Data ---  	ADD_TILE_DATA_EDITOR_GROUP("Custom Data");  	for (int i = 0; i < tile_set->get_custom_data_layers_count(); i++) { -		if (tile_set->get_custom_data_layer_name(i).is_empty()) { -			ADD_TILE_DATA_EDITOR(group, vformat("Custom Data %d", i), vformat("custom_data_%d", i)); +		String editor_name = vformat("custom_data_%d", i); +		String prop_name = tile_set->get_custom_data_layer_name(i); +		Variant::Type prop_type = tile_set->get_custom_data_layer_type(i); + +		if (prop_name.is_empty()) { +			ADD_TILE_DATA_EDITOR(group, vformat("Custom Data %d", i), editor_name);  		} else { -			ADD_TILE_DATA_EDITOR(group, tile_set->get_custom_data_layer_name(i), vformat("custom_data_%d", i)); +			ADD_TILE_DATA_EDITOR(group, prop_name, editor_name); +		} + +		// If the type of the edited property has been changed, delete the +		// editor and create a new one. +		if (tile_data_editors.has(editor_name) && ((TileDataDefaultEditor *)tile_data_editors[editor_name])->get_property_type() != prop_type) { +			tile_data_editors[vformat("custom_data_%d", i)]->queue_free(); +			tile_data_editors.erase(vformat("custom_data_%d", i));  		} -		if (!tile_data_editors.has(vformat("custom_data_%d", i))) { +		if (!tile_data_editors.has(editor_name)) {  			TileDataDefaultEditor *tile_data_custom_data_editor = memnew(TileDataDefaultEditor());  			tile_data_custom_data_editor->hide(); -			tile_data_custom_data_editor->setup_property_editor(tile_set->get_custom_data_layer_type(i), vformat("custom_data_%d", i), tile_set->get_custom_data_layer_name(i)); +			tile_data_custom_data_editor->setup_property_editor(prop_type, editor_name, prop_name);  			tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::queue_redraw));  			tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::queue_redraw)); -			tile_data_editors[vformat("custom_data_%d", i)] = tile_data_custom_data_editor; +			tile_data_editors[editor_name] = tile_data_custom_data_editor;  		}  	}  	for (int i = tile_set->get_custom_data_layers_count(); tile_data_editors.has(vformat("custom_data_%d", i)); i++) { diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 5e25d343b0..eaae9555dc 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -701,7 +701,7 @@ TileSetEditor::TileSetEditor() {  	source_sort_button = memnew(MenuButton);  	source_sort_button->set_flat(true); -	source_sort_button->set_tooltip_text(TTR("Sort sources")); +	source_sort_button->set_tooltip_text(TTR("Sort Sources"));  	PopupMenu *p = source_sort_button->get_popup();  	p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort)); @@ -801,6 +801,7 @@ TileSetEditor::TileSetEditor() {  	patterns_help_label = memnew(Label);  	patterns_help_label->set_text(TTR("Add new patterns in the TileMap editing mode.")); +	patterns_help_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);  	patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);  	patterns_item_list->add_child(patterns_help_label); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c8f6a2431d..a052d8860e 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -3623,12 +3623,6 @@ void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos, VisualShaderNod  	node_filter->select_all();  } -void VisualShaderEditor::_show_varying_menu() { -	varying_options->set_item_disabled(int(VaryingMenuOptions::REMOVE), visual_shader->get_varyings_count() == 0); -	varying_options->set_position(graph->get_screen_position() + varying_button->get_position() + Size2(0, varying_button->get_size().height)); -	varying_options->popup(); -} -  void VisualShaderEditor::_varying_menu_id_pressed(int p_idx) {  	switch (VaryingMenuOptions(p_idx)) {  		case VaryingMenuOptions::ADD: { @@ -4334,7 +4328,7 @@ void VisualShaderEditor::_update_varying_tree() {  		}  	} -	varying_options->set_item_disabled(int(VaryingMenuOptions::REMOVE), count == 0); +	varying_button->get_popup()->set_item_disabled(int(VaryingMenuOptions::REMOVE), count == 0);  }  void VisualShaderEditor::_varying_create() { @@ -4809,17 +4803,15 @@ VisualShaderEditor::VisualShaderEditor() {  	graph->get_zoom_hbox()->move_child(add_node, 0);  	add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog).bind(false, VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PORT_TYPE_MAX)); -	varying_button = memnew(Button); -	varying_button->set_flat(true); +	varying_button = memnew(MenuButton);  	varying_button->set_text(TTR("Manage Varyings")); +	varying_button->set_switch_on_hover(true);  	graph->get_zoom_hbox()->add_child(varying_button); -	varying_button->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_varying_menu)); -	varying_options = memnew(PopupMenu); -	add_child(varying_options); -	varying_options->add_item(TTR("Add Varying"), int(VaryingMenuOptions::ADD)); -	varying_options->add_item(TTR("Remove Varying"), int(VaryingMenuOptions::REMOVE)); -	varying_options->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_varying_menu_id_pressed)); +	PopupMenu *varying_menu = varying_button->get_popup(); +	varying_menu->add_item(TTR("Add Varying"), int(VaryingMenuOptions::ADD)); +	varying_menu->add_item(TTR("Remove Varying"), int(VaryingMenuOptions::REMOVE)); +	varying_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_varying_menu_id_pressed));  	preview_shader = memnew(Button);  	preview_shader->set_flat(true); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 5e21215738..e673051eb3 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -165,8 +165,7 @@ class VisualShaderEditor : public VBoxContainer {  	Ref<VisualShader> visual_shader;  	GraphEdit *graph = nullptr;  	Button *add_node = nullptr; -	Button *varying_button = nullptr; -	PopupMenu *varying_options = nullptr; +	MenuButton *varying_button = nullptr;  	Button *preview_shader = nullptr;  	OptionButton *edit_type = nullptr; @@ -283,7 +282,6 @@ class VisualShaderEditor : public VBoxContainer {  	void _tools_menu_option(int p_idx);  	void _show_members_dialog(bool at_mouse_pos, VisualShaderNode::PortType p_input_port_type = VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PortType p_output_port_type = VisualShaderNode::PORT_TYPE_MAX); -	void _show_varying_menu();  	void _varying_menu_id_pressed(int p_idx);  	void _show_add_varying_dialog();  	void _show_remove_varying_dialog(); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 8de2833240..3b764a9466 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -465,6 +465,7 @@ static const char *gdscript_function_renames[][2] = {  	{ "post_import", "_post_import" }, // EditorScenePostImport  	{ "print_stray_nodes", "print_orphan_nodes" }, // Node  	{ "property_list_changed_notify", "notify_property_list_changed" }, // Object +	{ "raise", "move_to_front" }, // CanvasItem  	{ "recognize", "_recognize" }, // ResourceFormatLoader  	{ "regen_normalmaps", "regen_normal_maps" }, // ArrayMesh  	{ "remove", "remove_at" }, // Array, broke Directory diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index afea682ecd..7c323a8524 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3517,6 +3517,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec  	button_tree_menu = memnew(MenuButton);  	button_tree_menu->set_flat(true); +	button_tree_menu->set_tooltip_text(TTR("Extra scene options."));  	button_tree_menu->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_update_tree_menu));  	filter_hbc->add_child(button_tree_menu); diff --git a/main/main.cpp b/main/main.cpp index 91d38ff6d9..9bd74f8afd 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1932,6 +1932,9 @@ error:  }  Error Main::setup2(Thread::ID p_main_tid_override) { +	// Print engine name and version +	print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE)); +  	engine->startup_benchmark_begin_measure("servers");  	tsman = memnew(TextServerManager); @@ -1949,9 +1952,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {  	initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);  	NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SERVERS); -	// Print engine name and version -	print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE)); -  	if (p_main_tid_override) {  		Thread::main_thread_id = p_main_tid_override;  	} diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index b58f002f3c..e717b501f4 100644 --- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -164,7 +164,7 @@  			};  			buildConfigurationList = D0BCFE2F18AEBDA2004A7AAE /* Build configuration list for PBXProject "$binary" */;  			compatibilityVersion = "Xcode 3.2"; -			developmentRegion = English; +			developmentRegion = en;  			hasScannedForEncodings = 0;  			knownRegions = (  				en, diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 92e5e59496..a002a37ab9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -299,7 +299,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC  			for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {  				plugin.onRegisterPluginWithGodotNative();  			} -			setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); +			setKeepScreenOn(Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));  		});  		// Include the returned non-null views in the Godot view hierarchy. diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 8e4d91ac50..fa86c88fa1 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -49,6 +49,46 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge  	return archs;  } +struct IconInfo { +	const char *preset_key; +	const char *idiom; +	const char *export_name; +	const char *actual_size_side; +	const char *scale; +	const char *unscaled_size; +	const bool force_opaque; +}; + +static const IconInfo icon_infos[] = { +	// Home screen on iPhone +	{ PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "2x", "60x60", false }, +	{ PNAME("icons/iphone_120x120"), "iphone", "Icon-120.png", "120", "3x", "40x40", false }, +	{ PNAME("icons/iphone_180x180"), "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + +	// Home screen on iPad +	{ PNAME("icons/ipad_76x76"), "ipad", "Icon-76.png", "76", "1x", "76x76", false }, +	{ PNAME("icons/ipad_152x152"), "ipad", "Icon-152.png", "152", "2x", "76x76", false }, +	{ PNAME("icons/ipad_167x167"), "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + +	// App Store +	{ PNAME("icons/app_store_1024x1024"), "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, + +	// Spotlight +	{ PNAME("icons/spotlight_40x40"), "ipad", "Icon-40.png", "40", "1x", "40x40", false }, +	{ PNAME("icons/spotlight_80x80"), "iphone", "Icon-80.png", "80", "2x", "40x40", false }, +	{ PNAME("icons/spotlight_80x80"), "ipad", "Icon-80.png", "80", "2x", "40x40", false }, + +	// Settings +	{ PNAME("icons/settings_58x58"), "iphone", "Icon-58.png", "58", "2x", "29x29", false }, +	{ PNAME("icons/settings_58x58"), "ipad", "Icon-58.png", "58", "2x", "29x29", false }, +	{ PNAME("icons/settings_87x87"), "iphone", "Icon-87.png", "87", "3x", "29x29", false }, + +	// Notification +	{ PNAME("icons/notification_40x40"), "iphone", "Icon-40.png", "40", "2x", "20x20", false }, +	{ PNAME("icons/notification_40x40"), "ipad", "Icon-40.png", "40", "2x", "20x20", false }, +	{ PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false } +}; +  struct LoadingScreenInfo {  	const char *preset_key;  	const char *export_name; @@ -139,18 +179,13 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));  	r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone/iPod Touch with Retina display -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone with Retina HD display - -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad with Retina display -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad Pro - -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // App Store - -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight -	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight on devices with Retina display - +	HashSet<String> used_names; +	for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) { +		if (!used_names.has(icon_infos[i].preset_key)) { +			used_names.insert(icon_infos[i].preset_key); +			r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, icon_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); +		} +	}  	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));  	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));  	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); @@ -531,36 +566,6 @@ void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p  	}  } -struct IconInfo { -	const char *preset_key; -	const char *idiom; -	const char *export_name; -	const char *actual_size_side; -	const char *scale; -	const char *unscaled_size; -	const bool force_opaque; -}; - -static const IconInfo icon_infos[] = { -	// Home screen on iPhone -	{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", false }, -	{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", false }, -	{ "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, - -	// Home screen on iPad -	{ "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false }, -	{ "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, -	{ "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, - -	// App Store -	{ "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, - -	// Spotlight -	{ "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, -	{ "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, -	{ "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } -}; -  Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {  	String json_description = "{\"images\":[";  	String sizes; @@ -568,6 +573,8 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr  	Ref<DirAccess> da = DirAccess::open(p_iconset_dir);  	ERR_FAIL_COND_V_MSG(da.is_null(), ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); +	Color boot_bg_color = GLOBAL_GET("application/boot_splash/bg_color"); +  	for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {  		IconInfo info = icon_infos[i];  		int side_size = String(info.actual_size_side).to_int(); @@ -580,13 +587,17 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr  			if (err != OK) {  				add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path));  				return ERR_UNCONFIGURED; +			} else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { +				add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); +				img->resize(side_size, side_size); +				Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); +				new_img->fill(boot_bg_color); +				_blend_and_rotate(new_img, img, false); +				err = new_img->save_png(p_iconset_dir + info.export_name); +			} else { +				img->resize(side_size, side_size); +				err = img->save_png(p_iconset_dir + info.export_name);  			} -			if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { -				add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); -				return ERR_UNCONFIGURED; -			} -			img->resize(side_size, side_size); -			err = img->save_png(p_iconset_dir + info.export_name);  			if (err) {  				add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path));  				return err; @@ -598,12 +609,14 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr  			if (err != OK) {  				add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path));  				return ERR_UNCONFIGURED; -			} -			if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { -				add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); -				return ERR_UNCONFIGURED; -			} -			if (img->get_width() != side_size || img->get_height() != side_size) { +			} else if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) { +				add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key)); +				img->resize(side_size, side_size); +				Ref<Image> new_img = Image::create_empty(side_size, side_size, false, Image::FORMAT_RGBA8); +				new_img->fill(boot_bg_color); +				_blend_and_rotate(new_img, img, false); +				err = new_img->save_png(p_iconset_dir + info.export_name); +			} else if (img->get_width() != side_size || img->get_height() != side_size) {  				add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2i(side_size, side_size)));  				img->resize(side_size, side_size);  				err = img->save_png(p_iconset_dir + info.export_name); diff --git a/platform/macos/godot_application_delegate.mm b/platform/macos/godot_application_delegate.mm index bacdcc2bc4..f1168c685a 100644 --- a/platform/macos/godot_application_delegate.mm +++ b/platform/macos/godot_application_delegate.mm @@ -61,7 +61,9 @@  - (void)applicationDidFinishLaunching:(NSNotification *)notice {  	NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; -	if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) { +	NSString *nsbundleid_env = [NSString stringWithUTF8String:getenv("__CFBundleIdentifier")]; +	NSString *nsbundleid = [[NSBundle mainBundle] bundleIdentifier]; +	if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO) || ![nsbundleid isEqualToString:nsbundleid_env]) {  		// If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).  		[self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];  	} diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 08897bb190..d8548eb545 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -849,7 +849,19 @@ String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold,  		if (FAILED(hr)) {  			continue;  		} -		return String::utf16((const char16_t *)&file_path[0]); +		String fpath = String::utf16((const char16_t *)&file_path[0]); + +		WIN32_FIND_DATAW d; +		HANDLE fnd = FindFirstFileW((LPCWSTR)&file_path[0], &d); +		if (fnd != INVALID_HANDLE_VALUE) { +			String fname = String::utf16((const char16_t *)d.cFileName); +			if (!fname.is_empty()) { +				fpath = fpath.get_base_dir().path_join(fname); +			} +			FindClose(fnd); +		} + +		return fpath;  	}  	return String();  } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index ea310f5a12..9e0dc049e5 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -1425,7 +1425,10 @@ bool CodeEdit::is_line_numbers_zero_padded() const {  }  void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { -	String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); +	String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding); +	if (is_localizing_numeral_system()) { +		fc = TS->format_number(fc); +	}  	Ref<TextLine> tl;  	tl.instantiate();  	tl->add_string(fc, font, font_size); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c5cb7157e8..4e76f72921 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2773,6 +2773,20 @@ bool Control::is_layout_rtl() const {  	return data.is_rtl;  } +void Control::set_localize_numeral_system(bool p_enable) { +	if (p_enable == data.localize_numeral_system) { +		return; +	} + +	data.localize_numeral_system = p_enable; + +	notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +} + +bool Control::is_localizing_numeral_system() const { +	return data.localize_numeral_system; +} +  void Control::set_auto_translate(bool p_enable) {  	if (p_enable == data.auto_translate) {  		return; @@ -3154,6 +3168,9 @@ void Control::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);  	ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating); +	ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system); +	ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system); +  	ADD_GROUP("Layout", "");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");  	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); @@ -3198,8 +3215,9 @@ void Control::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags");  	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); -	ADD_GROUP("Auto Translate", ""); +	ADD_GROUP("Localization", "");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "localize_numeral_system"), "set_localize_numeral_system", "is_localizing_numeral_system");  	ADD_GROUP("Tooltip", "tooltip_");  	ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 72e870930d..3e9bb48a4a 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -249,6 +249,7 @@ private:  		bool is_rtl = false;  		bool auto_translate = true; +		bool localize_numeral_system = true;  		// Extra properties. @@ -595,6 +596,9 @@ public:  	LayoutDirection get_layout_direction() const;  	virtual bool is_layout_rtl() const; +	void set_localize_numeral_system(bool p_enable); +	bool is_localizing_numeral_system() const; +  	void set_auto_translate(bool p_enable);  	bool is_auto_translating() const;  	_FORCE_INLINE_ String atr(const String p_string) const { diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index cade65108c..11a3803b35 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -632,8 +632,11 @@ void FileDialog::update_file_list() {  		files.pop_front();  	} -	if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { -		tree->get_root()->get_first_child()->select(0); +	if (mode != FILE_MODE_SAVE_FILE) { +		// Select the first file from list if nothing is selected. +		if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { +			tree->get_root()->get_first_child()->select(0); +		}  	}  } diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 8369eaa227..50bcfa6a0c 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -103,7 +103,12 @@ void ProgressBar::_notification(int p_what) {  			}  			if (show_percentage) { -				String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); +				String txt = itos(int(get_as_ratio() * 100)); +				if (is_localizing_numeral_system()) { +					txt = TS->format_number(txt) + TS->percent_sign(); +				} else { +					txt += String("%"); +				}  				TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size);  				Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 87cc26187a..889610e071 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -752,7 +752,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o  			prefix = _prefix;  			break;  		} else if (list_items[i]->list_type == LIST_NUMBERS) { -			segment = TS->format_number(itos(list_index[i]), _find_language(l.from)); +			segment = itos(list_index[i]); +			if (is_localizing_numeral_system()) { +				segment = TS->format_number(segment, _find_language(l.from)); +			}  		} else if (list_items[i]->list_type == LIST_LETTERS) {  			segment = _letters(list_index[i], list_items[i]->capitalize);  		} else if (list_items[i]->list_type == LIST_ROMAN) { @@ -2686,6 +2689,7 @@ bool RichTextLabel::_validate_line_caches() {  		int ctrl_height = get_size().height;  		// Update fonts. +		float old_scroll = vscroll->get_value();  		if (main->first_invalid_font_line.load() != (int)main->lines.size()) {  			for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) {  				_update_line_font(main, i, theme_cache.normal_font, theme_cache.normal_font_size); @@ -2695,6 +2699,7 @@ bool RichTextLabel::_validate_line_caches() {  		}  		if (main->first_resized_line.load() == (int)main->lines.size()) { +			vscroll->set_value(old_scroll);  			return true;  		} @@ -2733,6 +2738,8 @@ bool RichTextLabel::_validate_line_caches() {  			vscroll->set_page(text_rect.size.height);  			if (scroll_follow && scroll_following) {  				vscroll->set_value(total_height); +			} else { +				vscroll->set_value(old_scroll);  			}  			updating_scroll = false; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index c4000120c8..e15b3b7bd4 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -40,7 +40,10 @@ Size2 SpinBox::get_minimum_size() const {  }  void SpinBox::_value_changed(double p_value) { -	String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step()))); +	String value = String::num(get_value(), Math::range_step_decimals(get_step())); +	if (is_localizing_numeral_system()) { +		value = TS->format_number(value); +	}  	if (!line_edit->has_focus()) {  		if (!prefix.is_empty()) { diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 1b06e09bb8..4afc82576d 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -169,7 +169,15 @@ Dictionary BitMap::_get_data() const {  	return d;  } -Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { +struct CrossStackEntry { +	Point2i cross; +	Vector<int> ranges; + +	_FORCE_INLINE_ bool operator==(const CrossStackEntry &p_other) const { return cross == p_other.cross; } +	_FORCE_INLINE_ bool operator!=(const CrossStackEntry &p_other) const { return cross != p_other.cross; } +}; + +Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const {  	int stepx = 0;  	int stepy = 0;  	int prevx = 0; @@ -179,9 +187,20 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta  	int curx = startx;  	int cury = starty;  	unsigned int count = 0; -	HashSet<Point2i> case9s; -	HashSet<Point2i> case6s; -	Vector<Vector2> _points; + +	Vector<CrossStackEntry> cross_stack; +	int cross_stack_size = 0; + +	// Add starting point to stack as the default entry. +	cross_stack.push_back({ Point2i(-1, -1), Vector<int>({ 0 }) }); +	cross_stack_size++; + +	Vector<Point2i> _points; +	Vector<Vector<Vector2>> ret; + +	// Add starting entry at start of return. +	ret.resize(1); +  	do {  		int sv = 0;  		{ // Square value @@ -202,7 +221,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta  			sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0;  			Point2i br = Point2i(curx, cury);  			sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; -			ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); +			ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector<Vector2>>());  		}  		switch (sv) { @@ -266,70 +285,139 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta  				stepy = 0;  				break;  			case 9: -				/* +				/* Going DOWN if coming from the LEFT, otherwise go UP. +				9  				+---+---+  				| 1 |   |  				+---+---+  				|   | 8 |  				+---+---+ -				this should normally go UP, but if we already been here, we go down  				*/ -				if (case9s.has(Point2i(curx, cury))) { -					//found, so we go down, and delete from case9s; + +				if (prevx == 1) {  					stepx = 0;  					stepy = 1; -					case9s.erase(Point2i(curx, cury));  				} else { -					//not found, we go up, and add to case9s;  					stepx = 0;  					stepy = -1; -					case9s.insert(Point2i(curx, cury));  				}  				break;  			case 6: -				/* +				/* Going RIGHT if coming from BELOW, otherwise go LEFT.  				6  				+---+---+  				|   | 2 |  				+---+---+  				| 4 |   |  				+---+---+ -				this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT  				*/ -				if (case6s.has(Point2i(curx, cury))) { -					//found, so we go left, and delete from case6s; -					stepx = -1; + +				if (prevy == -1) { +					stepx = 1;  					stepy = 0; -					case6s.erase(Point2i(curx, cury));  				} else { -					//not found, we go right, and add to case6s; -					stepx = 1; +					stepx = -1;  					stepy = 0; -					case6s.insert(Point2i(curx, cury));  				}  				break;  			default:  				ERR_PRINT("this shouldn't happen.");  		} + +		// Handle crossing points. +		if (sv == 6 || sv == 9) { +			const int new_index = _points.size() - 1; + +			// Add previous point to last stack entry. +			cross_stack.write[cross_stack_size - 1].ranges.push_back(new_index); + +			// Create temporary entry to maybe insert, for searching. +			const CrossStackEntry new_entry = { _points[new_index], Vector<int>({ new_index }) }; + +			// Attempt to find matching entry. +			const int found = cross_stack.rfind(new_entry, cross_stack_size - 1); + +			if (found != -1) { +				Vector<Vector2> tmp; + +				// Iterate over entries between end of stack and found, adding ranges to result. +				for (int i = found; i < cross_stack_size; i++) { +					const Vector<int> &ranges = cross_stack[i].ranges; + +					for (int j = 0; j < ranges.size() / 2; j++) { +						int first = ranges[2 * j]; +						const int last = ranges[2 * j + 1]; + +						int new_pos = tmp.size(); + +						tmp.resize(tmp.size() + (last - first)); + +						Vector2 *tmp_ptrw = tmp.ptrw(); + +						for (; first < last; first++, new_pos++) { +							tmp_ptrw[new_pos].x = (float)(_points[first].x - p_rect.position.x); +							tmp_ptrw[new_pos].y = (float)(_points[first].y - p_rect.position.y); +						} +					} +				} + +				ret.push_back(tmp); + +				// Shrink stack. +				cross_stack_size = found; + +				// Add previous point to last stack entry. +				cross_stack.write[cross_stack_size - 1].ranges.push_back(new_index); +			} else { +				cross_stack.resize(MAX(cross_stack_size + 1, cross_stack.size())); +				cross_stack.set(cross_stack_size, new_entry); +				cross_stack_size++; +			} +		} +  		// Small optimization:  		// If the previous direction is same as the current direction,  		// then we should modify the last vector to current.  		curx += stepx;  		cury += stepy;  		if (stepx == prevx && stepy == prevy) { -			_points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); -			_points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); +			_points.write[_points.size() - 1].x = curx; +			_points.write[_points.size() - 1].y = cury;  		} else { -			_points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); +			_points.push_back(Point2i(curx, cury));  		}  		count++;  		prevx = stepx;  		prevy = stepy; -		ERR_FAIL_COND_V((int)count > width * height, _points); +		ERR_FAIL_COND_V((int)count > width * height, Vector<Vector<Vector2>>());  	} while (curx != startx || cury != starty); -	return _points; + +	// Add last position to last stack entry. +	cross_stack.write[cross_stack_size - 1].ranges.push_back(_points.size()); + +	for (int i = 0; i < cross_stack_size; i++) { +		const Vector<int> &ranges = cross_stack[i].ranges; + +		for (int j = 0; j < ranges.size() / 2; j++) { +			int first = ranges[2 * j]; +			const int last = ranges[2 * j + 1]; + +			int new_pos = ret[0].size(); + +			ret.write[0].resize(ret[0].size() + (last - first)); + +			Vector2 *tmp_ptrw = ret.write[0].ptrw(); + +			for (; first < last; first++, new_pos++) { +				tmp_ptrw[new_pos].x = (float)(_points[first].x - p_rect.position.x); +				tmp_ptrw[new_pos].y = (float)(_points[first].y - p_rect.position.y); +			} +		} +	} + +	return ret;  }  static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { @@ -442,7 +530,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_  			for (int j = next_j; j <= pos.y + 1; j++) {  				if (popped) {  					// The next loop over j must start normally. -					next_j = pos.y; +					next_j = pos.y - 1;  					popped = false;  					// Skip because an iteration was already executed with current counter values.  					continue; @@ -486,13 +574,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_  			}  		}  	} while (reenter || popped); - -	print_verbose("BitMap: Max stack size: " + itos(stack.size()));  }  Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const {  	Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); -	print_verbose("BitMap: Rect: " + r);  	Point2i from;  	Ref<BitMap> fill; @@ -505,17 +590,16 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, fl  			if (!fill->get_bit(j, i) && get_bit(j, i)) {  				fill_bits(this, fill, Point2i(j, i), r); -				Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); -				print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); -				polygon = reduce(polygon, r, p_epsilon); -				print_verbose("BitMap: Post reduce: " + itos(polygon.size())); +				for (Vector<Vector2> polygon : _march_square(r, Point2i(j, i))) { +					polygon = reduce(polygon, r, p_epsilon); -				if (polygon.size() < 3) { -					print_verbose("Invalid polygon, skipped"); -					continue; -				} +					if (polygon.size() < 3) { +						print_verbose("Invalid polygon, skipped"); +						continue; +					} -				polygons.push_back(polygon); +					polygons.push_back(polygon); +				}  			}  		}  	} diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 291ed8c4d0..0ec5772fd1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -46,7 +46,7 @@ class BitMap : public Resource {  	int width = 0;  	int height = 0; -	Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; +	Vector<Vector<Vector2>> _march_square(const Rect2i &p_rect, const Point2i &p_start) const;  	TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; diff --git a/servers/rendering/dummy/storage/mesh_storage.cpp b/servers/rendering/dummy/storage/mesh_storage.cpp index adf736eee3..52de998835 100644 --- a/servers/rendering/dummy/storage/mesh_storage.cpp +++ b/servers/rendering/dummy/storage/mesh_storage.cpp @@ -56,3 +56,10 @@ void MeshStorage::mesh_free(RID p_rid) {  	mesh_owner.free(p_rid);  } + +void MeshStorage::mesh_clear(RID p_mesh) { +	DummyMesh *m = mesh_owner.get_or_null(p_mesh); +	ERR_FAIL_COND(!m); + +	m->surfaces.clear(); +} diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index 4399f4ab66..28fe8cb80d 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -116,7 +116,7 @@ public:  	virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override { return AABB(); }  	virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override {} -	virtual void mesh_clear(RID p_mesh) override {} +	virtual void mesh_clear(RID p_mesh) override;  	/* MESH INSTANCE */ diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 0ab21bc4ef..a05db8c563 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -510,16 +510,18 @@ void CopyEffects::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuff  	memset(©_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); -	copy_to_fb.push_constant.use_section = true; +	copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_USE_SECTION;  	copy_to_fb.push_constant.section[0] = p_uv_rect.position.x;  	copy_to_fb.push_constant.section[1] = p_uv_rect.position.y;  	copy_to_fb.push_constant.section[2] = p_uv_rect.size.x;  	copy_to_fb.push_constant.section[3] = p_uv_rect.size.y;  	if (p_flip_y) { -		copy_to_fb.push_constant.flip_y = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_FLIP_Y;  	} +	copy_to_fb.push_constant.luminance_multiplier = 1.0; +  	// setup our uniforms  	RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -537,28 +539,35 @@ void CopyEffects::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuff  	RD::get_singleton()->draw_list_draw(draw_list, true);  } -void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one) { +void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview, bool p_alpha_to_one, bool p_linear) {  	UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();  	ERR_FAIL_NULL(uniform_set_cache);  	MaterialStorage *material_storage = MaterialStorage::get_singleton();  	ERR_FAIL_NULL(material_storage);  	memset(©_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); +	copy_to_fb.push_constant.luminance_multiplier = 1.0;  	if (p_flip_y) { -		copy_to_fb.push_constant.flip_y = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_FLIP_Y;  	}  	if (p_force_luminance) { -		copy_to_fb.push_constant.force_luminance = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_FORCE_LUMINANCE;  	}  	if (p_alpha_to_zero) { -		copy_to_fb.push_constant.alpha_to_zero = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_ALPHA_TO_ZERO;  	}  	if (p_srgb) { -		copy_to_fb.push_constant.srgb = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_SRGB;  	}  	if (p_alpha_to_one) { -		copy_to_fb.push_constant.alpha_to_one = true; +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_ALPHA_TO_ONE; +	} +	if (p_linear) { +		// Used for copying to a linear buffer. In the mobile renderer we divide the contents of the linear buffer +		// to allow for a wider effective range. +		copy_to_fb.push_constant.flags |= COPY_TO_FB_FLAG_LINEAR; +		copy_to_fb.push_constant.luminance_multiplier = prefer_raster_effects ? 2.0 : 1.0;  	}  	// setup our uniforms diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index cda4f70730..83f7a51a36 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -181,16 +181,21 @@ private:  		COPY_TO_FB_MAX,  	}; +	enum CopyToFBFlags { +		COPY_TO_FB_FLAG_FLIP_Y = (1 << 0), +		COPY_TO_FB_FLAG_USE_SECTION = (1 << 1), +		COPY_TO_FB_FLAG_FORCE_LUMINANCE = (1 << 2), +		COPY_TO_FB_FLAG_ALPHA_TO_ZERO = (1 << 3), +		COPY_TO_FB_FLAG_SRGB = (1 << 4), +		COPY_TO_FB_FLAG_ALPHA_TO_ONE = (1 << 5), +		COPY_TO_FB_FLAG_LINEAR = (1 << 6), +	}; +  	struct CopyToFbPushConstant {  		float section[4];  		float pixel_size[2]; -		uint32_t flip_y; -		uint32_t use_section; - -		uint32_t force_luminance; -		uint32_t alpha_to_zero; -		uint32_t srgb; -		uint32_t alpha_to_one; +		float luminance_multiplier; +		uint32_t flags;  		float set_color[4];  	}; @@ -322,7 +327,7 @@ public:  	void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);  	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);  	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far); -	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false); +	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false);  	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);  	void copy_raster(RID p_source_texture, RID p_dest_framebuffer); diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 5ff00aa94c..701d53b41d 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -92,18 +92,15 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi  }  Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const { -	// TODO we should find some way to store this properly, we're assuming 16x16 as this seems to be the standard but in our vrs_capacities we -	// obtain a minimum and maximum size, and we should choose something within this range and then make sure that is consistently set when creating -	// our frame buffer. Also it is important that we make the resulting size we calculate down below available to the end user so they know the size -	// of the VRS buffer to supply. -	Size2i texel_size = Size2i(16, 16); - -	int width = p_base_size.x / texel_size.x; -	if (p_base_size.x % texel_size.x != 0) { +	int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH); +	int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT); + +	int width = p_base_size.x / texel_width; +	if (p_base_size.x % texel_width != 0) {  		width++;  	} -	int height = p_base_size.y / texel_size.y; -	if (p_base_size.y % texel_size.y != 0) { +	int height = p_base_size.y / texel_height; +	if (p_base_size.y % texel_height != 0) {  		height++;  	}  	return Size2i(width, height); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 2d1d0e0951..41fceac7c2 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1765,6 +1765,10 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co  				draw_sky = true;  			} break;  			case RS::ENV_BG_CANVAS: { +				if (rb.is_valid()) { +					RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); +					copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true); +				}  				keep_color = true;  			} break;  			case RS::ENV_BG_KEEP: { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 558aba62e1..2506f4578b 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -796,6 +796,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color  				draw_sky = true;  			} break;  			case RS::ENV_BG_CANVAS: { +				if (rb.is_valid()) { +					RID dest_framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS); +					RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); +					copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true); +				}  				keep_color = true;  			} break;  			case RS::ENV_BG_KEEP: { diff --git a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl index 4d4e983b7f..46bb99794d 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl @@ -13,6 +13,14 @@  #endif // has_VK_KHR_multiview  #endif //MULTIVIEW +#define FLAG_FLIP_Y (1 << 0) +#define FLAG_USE_SECTION (1 << 1) +#define FLAG_FORCE_LUMINANCE (1 << 2) +#define FLAG_ALPHA_TO_ZERO (1 << 3) +#define FLAG_SRGB (1 << 4) +#define FLAG_ALPHA_TO_ONE (1 << 5) +#define FLAG_LINEAR (1 << 6) +  #ifdef MULTIVIEW  layout(location = 0) out vec3 uv_interp;  #else @@ -22,13 +30,8 @@ layout(location = 0) out vec2 uv_interp;  layout(push_constant, std430) uniform Params {  	vec4 section;  	vec2 pixel_size; -	bool flip_y; -	bool use_section; - -	bool force_luminance; -	bool alpha_to_zero; -	bool srgb; -	bool alpha_to_one; +	float luminance_multiplier; +	uint flags;  	vec4 color;  } @@ -41,13 +44,13 @@ void main() {  	uv_interp.z = ViewIndex;  #endif  	vec2 vpos = uv_interp.xy; -	if (params.use_section) { +	if (bool(params.flags & FLAG_USE_SECTION)) {  		vpos = params.section.xy + vpos * params.section.zw;  	}  	gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0); -	if (params.flip_y) { +	if (bool(params.flags & FLAG_FLIP_Y)) {  		uv_interp.y = 1.0 - uv_interp.y;  	}  } @@ -67,16 +70,19 @@ void main() {  #endif // has_VK_KHR_multiview  #endif //MULTIVIEW +#define FLAG_FLIP_Y (1 << 0) +#define FLAG_USE_SECTION (1 << 1) +#define FLAG_FORCE_LUMINANCE (1 << 2) +#define FLAG_ALPHA_TO_ZERO (1 << 3) +#define FLAG_SRGB (1 << 4) +#define FLAG_ALPHA_TO_ONE (1 << 5) +#define FLAG_LINEAR (1 << 6) +  layout(push_constant, std430) uniform Params {  	vec4 section;  	vec2 pixel_size; -	bool flip_y; -	bool use_section; - -	bool force_luminance; -	bool alpha_to_zero; -	bool srgb; -	bool alpha_to_one; +	float luminance_multiplier; +	uint flags;  	vec4 color;  } @@ -110,6 +116,10 @@ vec3 linear_to_srgb(vec3 color) {  	return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));  } +vec3 srgb_to_linear(vec3 color) { +	return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045))); +} +  void main() {  #ifdef MODE_SET_COLOR  	frag_color = params.color; @@ -165,19 +175,22 @@ void main() {  #endif /* MODE_TWO_SOURCES */  #endif /* MULTIVIEW */ -	if (params.force_luminance) { +	if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {  		color.rgb = vec3(max(max(color.r, color.g), color.b));  	} -	if (params.alpha_to_zero) { +	if (bool(params.flags & FLAG_ALPHA_TO_ZERO)) {  		color.rgb *= color.a;  	} -	if (params.srgb) { +	if (bool(params.flags & FLAG_SRGB)) {  		color.rgb = linear_to_srgb(color.rgb);  	} -	if (params.alpha_to_one) { +	if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {  		color.a = 1.0;  	} +	if (bool(params.flags & FLAG_LINEAR)) { +		color.rgb = srgb_to_linear(color.rgb); +	} -	frag_color = color; +	frag_color = color / params.luminance_multiplier;  #endif // MODE_SET_COLOR  } diff --git a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl index 5ef83c0b44..b450bb9fe9 100644 --- a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl @@ -63,10 +63,18 @@ void main() {  #ifdef MULTIVIEW  	vec4 color = textureLod(source_color, uv, 0.0); +	frag_color = uint(color.r * 255.0);  #else /* MULTIVIEW */  	vec4 color = textureLod(source_color, uv, 0.0); -#endif /* MULTIVIEW */ -	// See if we can change the sampler to one that returns int... -	frag_color = uint(color.r * 256.0); +	// for user supplied VRS map we do a color mapping +	color.r *= 3.0; +	frag_color = int(color.r) << 2; + +	color.g *= 3.0; +	frag_color += int(color.g); + +	// note 1x4, 4x1, 1x8, 8x1, 2x8 and 8x2 are not supported +	// 4x8, 8x4 and 8x8 are only available on some GPUs +#endif /* MULTIVIEW */  } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index bea2a80890..15c5687665 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -560,6 +560,7 @@ void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS:  void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) {  	CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture);  	ERR_FAIL_NULL(ct); +  	ct->texture_repeat = p_repeat;  	ct->clear_sets();  } diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 348f6e4695..4ee355ee9f 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -3319,7 +3319,7 @@ void RendererSceneCull::render_empty_scene(const Ref<RenderSceneBuffers> &p_rend  	RendererSceneRender::CameraData camera_data;  	camera_data.set_camera(Transform3D(), Projection(), true, false); -	scene_render->render_scene(p_render_buffers, &camera_data, &camera_data, PagedArray<RenderGeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); +	scene_render->render_scene(p_render_buffers, &camera_data, &camera_data, PagedArray<RenderGeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), environment, RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr);  #endif  } diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 2836889de5..8cec531393 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -550,6 +550,9 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {  			if (!can_draw_3d) {  				RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);  			} else { +				// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn. +				// Clear now otherwise we copy over garbage from the render target. +				RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);  				_draw_3d(p_viewport);  			}  		} diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index d0b85d8220..d99cc9a350 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1257,6 +1257,8 @@ public:  		LIMIT_SUBGROUP_SIZE,  		LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc.  		LIMIT_SUBGROUP_OPERATIONS, +		LIMIT_VRS_TEXEL_WIDTH, +		LIMIT_VRS_TEXEL_HEIGHT,  	};  	virtual uint64_t limit_get(Limit p_limit) const = 0; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 497e4476d0..62f061c9c0 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -8190,25 +8190,27 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f  			};  				[[fallthrough]];  			case TK_INSTANCE: { +				if (tk.type == TK_INSTANCE) {  #ifdef DEBUG_ENABLED -				keyword_completion_context = CF_UNIFORM_KEYWORD; -				if (_lookup_next(next)) { -					if (next.type == TK_UNIFORM) { -						keyword_completion_context ^= CF_UNIFORM_KEYWORD; +					keyword_completion_context = CF_UNIFORM_KEYWORD; +					if (_lookup_next(next)) { +						if (next.type == TK_UNIFORM) { +							keyword_completion_context ^= CF_UNIFORM_KEYWORD; +						}  					} -				}  #endif // DEBUG_ENABLED -				if (String(shader_type_identifier) != "spatial") { -					_set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier)); -					return ERR_PARSE_ERROR; -				} -				if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) { -					tk = _get_token(); -					if (tk.type != TK_UNIFORM) { -						_set_expected_after_error("uniform", "instance"); +					if (String(shader_type_identifier) != "spatial") { +						_set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier));  						return ERR_PARSE_ERROR;  					} -					uniform_scope = ShaderNode::Uniform::SCOPE_INSTANCE; +					if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) { +						tk = _get_token(); +						if (tk.type != TK_UNIFORM) { +							_set_expected_after_error("uniform", "instance"); +							return ERR_PARSE_ERROR; +						} +						uniform_scope = ShaderNode::Uniform::SCOPE_INSTANCE; +					}  				}  			};  				[[fallthrough]]; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 4d58d24405..ec4ae98397 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -167,11 +167,12 @@ RID XRInterface::get_vrs_texture() {  	// Default logic will return a standard VRS image based on our target size and default projections.  	// Note that this only gets called if VRS is supported on the hardware. -	Size2 texel_size = Size2(16.0, 16.0); // For now we assume we always use 16x16 texels, seems to be the standard. +	int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH); +	int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT);  	int view_count = get_view_count();  	Size2 target_size = get_render_target_size();  	real_t aspect = target_size.x / target_size.y; // is this y/x ? -	Size2 vrs_size = Size2(round(0.5 + target_size.x / texel_size.x), round(0.5 + target_size.y / texel_size.y)); +	Size2 vrs_size = Size2(round(0.5 + target_size.x / texel_width), round(0.5 + target_size.y / texel_height));  	real_t radius = vrs_size.length() * 0.5;  	Size2 vrs_sizei = vrs_size; @@ -179,6 +180,8 @@ RID XRInterface::get_vrs_texture() {  		const uint8_t densities[] = {  			0, // 1x1  			1, // 1x2 +			// 2, // 1x4 - not supported +			// 3, // 1x8 - not supported  			// 4, // 2x1  			5, // 2x2  			6, // 2x4 diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index aca7e5fe22..dc47bd7863 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -434,6 +434,37 @@ TEST_CASE("[BitMap] Clip to polygon") {  	polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128));  	CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon");  	CHECK_MESSAGE(polygons[0].size() == 6, "The polygon should have exactly 6 points"); + +	reset_bit_map(bit_map); +	bit_map.set_bit_rect(Rect2i(0, 0, 64, 64), true); +	bit_map.set_bit_rect(Rect2i(64, 64, 64, 64), true); +	bit_map.set_bit_rect(Rect2i(192, 128, 64, 64), true); +	bit_map.set_bit_rect(Rect2i(128, 192, 64, 64), true); +	polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 256, 256)); +	CHECK_MESSAGE(polygons.size() == 4, "We should have exactly 4 polygons"); +	CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); +	CHECK_MESSAGE(polygons[1].size() == 4, "The polygon should have exactly 4 points"); +	CHECK_MESSAGE(polygons[2].size() == 4, "The polygon should have exactly 4 points"); +	CHECK_MESSAGE(polygons[3].size() == 4, "The polygon should have exactly 4 points"); + +	reset_bit_map(bit_map); +	bit_map.set_bit(0, 0, true); +	bit_map.set_bit(2, 0, true); +	bit_map.set_bit_rect(Rect2i(1, 1, 1, 2), true); +	polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 3, 3)); +	CHECK_MESSAGE(polygons.size() == 3, "We should have exactly 3 polygons"); +	CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); +	CHECK_MESSAGE(polygons[1].size() == 4, "The polygon should have exactly 4 points"); +	CHECK_MESSAGE(polygons[2].size() == 4, "The polygon should have exactly 4 points"); + +	reset_bit_map(bit_map); +	bit_map.set_bit_rect(Rect2i(0, 0, 2, 1), true); +	bit_map.set_bit_rect(Rect2i(0, 2, 3, 1), true); +	bit_map.set_bit(0, 1, true); +	bit_map.set_bit(2, 1, true); +	polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 4, 4)); +	CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); +	CHECK_MESSAGE(polygons[0].size() == 6, "The polygon should have exactly 6 points");  }  } // namespace TestBitmap  |