summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml7
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--core/io/pck_packer.cpp2
-rw-r--r--doc/classes/ArrayMesh.xml12
-rw-r--r--doc/classes/Control.xml4
-rwxr-xr-xdoc/tools/make_rst.py169
-rw-r--r--drivers/gles3/storage/texture_storage.cpp8
-rw-r--r--editor/animation_track_editor.cpp5
-rw-r--r--editor/animation_track_editor.h2
-rw-r--r--editor/code_editor.cpp8
-rw-r--r--editor/editor_help.cpp50
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/filesystem_dock.cpp6
-rw-r--r--editor/inspector_dock.cpp4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp5
-rw-r--r--editor/plugins/tiles/tile_data_editors.h2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp1
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp23
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp3
-rw-r--r--editor/scene_tree_dock.cpp1
-rw-r--r--main/main.cpp6
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java2
-rw-r--r--platform/ios/export/export_plugin.cpp121
-rw-r--r--platform/macos/godot_application_delegate.mm4
-rw-r--r--platform/windows/os_windows.cpp14
-rw-r--r--scene/gui/code_edit.cpp5
-rw-r--r--scene/gui/control.cpp20
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/file_dialog.cpp7
-rw-r--r--scene/gui/progress_bar.cpp7
-rw-r--r--scene/gui/rich_text_label.cpp9
-rw-r--r--scene/gui/spin_box.cpp5
-rw-r--r--scene/resources/bit_map.cpp162
-rw-r--r--scene/resources/bit_map.h2
-rw-r--r--servers/rendering/dummy/storage/mesh_storage.cpp7
-rw-r--r--servers/rendering/dummy/storage/mesh_storage.h2
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp25
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.h21
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp5
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl55
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp1
-rw-r--r--servers/rendering/renderer_scene_cull.cpp2
-rw-r--r--servers/rendering/renderer_viewport.cpp3
-rw-r--r--tests/scene/test_bit_map.h31
46 files changed, 648 insertions, 196 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&lt;Vector3&gt;();
- 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/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/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(&copy_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(&copy_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/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/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/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