summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--core/io/ip.cpp8
-rw-r--r--core/io/json.cpp5
-rw-r--r--core/os/thread.cpp6
-rw-r--r--core/os/thread.h2
-rw-r--r--core/typedefs.h4
-rw-r--r--doc/classes/DisplayServer.xml12
-rw-r--r--doc/classes/EditorPaths.xml4
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp28
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp17
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp2
-rw-r--r--editor/connections_dialog.cpp3
-rw-r--r--editor/editor_paths.cpp14
-rw-r--r--editor/editor_themes.cpp16
-rw-r--r--editor/localization_editor.cpp26
-rw-r--r--editor/localization_editor.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp1
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp6
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h1
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp18
-rw-r--r--editor/plugins/script_editor_plugin.cpp65
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/pot_generator.cpp6
-rw-r--r--editor/project_converter_3_to_4.cpp4
-rw-r--r--editor/renames_map_3_to_4.cpp9
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj2
-rw-r--r--modules/mono/godotsharp_dirs.cpp22
-rw-r--r--modules/text_server_adv/text_server_adv.cpp23
-rw-r--r--modules/text_server_fb/text_server_fb.cpp6
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp30
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp23
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h2
-rw-r--r--platform/macos/detect.py8
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm30
-rw-r--r--platform/windows/display_server_windows.cpp36
-rw-r--r--platform/windows/display_server_windows.h5
-rw-r--r--scene/3d/cpu_particles_3d.cpp2
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/gui/code_edit.cpp6
-rw-r--r--scene/gui/subviewport_container.cpp22
-rw-r--r--scene/gui/subviewport_container.h1
-rw-r--r--scene/main/viewport.cpp16
-rw-r--r--scene/main/viewport.h1
-rw-r--r--scene/resources/default_theme/default_theme.cpp4
-rw-r--r--scene/resources/texture.cpp6
-rw-r--r--servers/display_server.cpp2
-rw-r--r--servers/display_server.h2
-rw-r--r--servers/rendering/renderer_canvas_render.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp28
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp17
-rw-r--r--servers/rendering/renderer_scene_cull.cpp1
56 files changed, 445 insertions, 136 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 9905353454..84557e3a64 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1356,6 +1356,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray());
+ GLOBAL_DEF_INTERNAL("internationalization/locale/translations_pot_files", PackedStringArray());
}
ProjectSettings::~ProjectSettings() {
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 28b7037120..65728f34f6 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -75,7 +75,7 @@ struct _IP_ResolverPrivate {
Semaphore sem;
Thread thread;
- bool thread_abort = false;
+ SafeFlag thread_abort;
void resolve_queues() {
for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) {
@@ -111,7 +111,7 @@ struct _IP_ResolverPrivate {
static void _thread_function(void *self) {
_IP_ResolverPrivate *ipr = static_cast<_IP_ResolverPrivate *>(self);
- while (!ipr->thread_abort) {
+ while (!ipr->thread_abort.is_set()) {
ipr->sem.wait();
ipr->resolve_queues();
}
@@ -343,12 +343,12 @@ IP::IP() {
singleton = this;
resolver = memnew(_IP_ResolverPrivate);
- resolver->thread_abort = false;
+ resolver->thread_abort.clear();
resolver->thread.start(_IP_ResolverPrivate::_thread_function, resolver);
}
IP::~IP() {
- resolver->thread_abort = true;
+ resolver->thread_abort.set();
resolver->sem.post();
resolver->thread.wait_to_finish();
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 448e39b2c3..8d0fe53ed4 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -91,9 +91,12 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::PACKED_STRING_ARRAY:
case Variant::ARRAY: {
+ Array a = p_var;
+ if (a.size() == 0) {
+ return "[]";
+ }
String s = "[";
s += end_statement;
- Array a = p_var;
ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON.");
p_markers.insert(a.id());
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 9d16392b2a..92865576f3 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -50,8 +50,8 @@ void Thread::_set_platform_functions(const PlatformFunctions &p_functions) {
platform_functions = p_functions;
}
-void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
- Thread::caller_id = _thread_id_hash(p_self->thread.get_id());
+void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) {
+ Thread::caller_id = p_caller_id;
if (platform_functions.set_priority) {
platform_functions.set_priority(p_settings.priority);
}
@@ -79,7 +79,7 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_
std::thread empty_thread;
thread.swap(empty_thread);
}
- std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
+ std::thread new_thread(&Thread::callback, _thread_id_hash(thread.get_id()), p_settings, p_callback, p_user);
thread.swap(new_thread);
id = _thread_id_hash(thread.get_id());
}
diff --git a/core/os/thread.h b/core/os/thread.h
index 3d4c48a760..6eb21fba65 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -82,7 +82,7 @@ private:
static thread_local ID caller_id;
std::thread thread;
- static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
+ static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
static PlatformFunctions platform_functions;
diff --git a/core/typedefs.h b/core/typedefs.h
index cd1367611a..1dcba58188 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -305,9 +305,9 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
#endif
// Macro GD_IS_DEFINED() allows to check if a macro is defined. It needs to be defined to anything (say 1) to work.
-#define __GDARG_PLACEHOLDER_1 0,
+#define __GDARG_PLACEHOLDER_1 false,
#define __gd_take_second_arg(__ignored, val, ...) val
-#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk 1, 0)
+#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk true, false)
#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val)
#define GD_IS_DEFINED(x) ___gd_is_defined(x)
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 4c2200ae9a..8bf58d5cd3 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -865,6 +865,15 @@
[b]Note:[/b] This method is implemented on Android and iOS.
</description>
</method>
+ <method name="screen_get_pixel" qualifiers="const">
+ <return type="Color" />
+ <param index="0" name="position" type="Vector2i" />
+ <description>
+ Returns color of the display pixel at the [param position].
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS, and Windows.
+ [b]Note:[/b] On macOS, this method requires "Screen Recording" permission, if permission is not granted it will return desktop wallpaper color.
+ </description>
+ </method>
<method name="screen_get_position" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="screen" type="int" default="-1" />
@@ -1543,6 +1552,9 @@
<constant name="FEATURE_EXTEND_TO_TITLE" value="20" enum="Feature">
Display server supports expanding window content to the title. See [constant WINDOW_FLAG_EXTEND_TO_TITLE]. [b]macOS[/b]
</constant>
+ <constant name="FEATURE_SCREEN_CAPTURE" value="21" enum="Feature">
+ Display server supports reading screen pixels. See [method screen_get_pixel].
+ </constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>
diff --git a/doc/classes/EditorPaths.xml b/doc/classes/EditorPaths.xml
index eacb366d84..c4b9eda24c 100644
--- a/doc/classes/EditorPaths.xml
+++ b/doc/classes/EditorPaths.xml
@@ -64,7 +64,9 @@
<return type="bool" />
<description>
Returns [code]true[/code] if the editor is marked as self-contained, [code]false[/code] otherwise. When self-contained mode is enabled, user configuration, data and cache files are saved in an [code]editor_data/[/code] folder next to the editor binary. This makes portable usage easier and ensures the Godot editor minimizes file writes outside its own folder. Self-contained mode is not available for exported projects.
- Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary while the editor is not running. See also [method get_self_contained_file].
+ Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary or macOS .app bundle while the editor is not running. See also [method get_self_contained_file].
+ [b]Note:[/b] On macOS, quarantine flag should be manually removed before using self-contained mode, see [url=https://docs.godotengine.org/en/stable/tutorials/export/running_on_macos.html]Running on macOS[/url].
+ [b]Note:[/b] On macOS, placing [code]_sc_[/code] or any other file inside .app bundle will break digital signature and make it non-portable, consider placing it in the same folder as the .app bundle instead.
[b]Note:[/b] The Steam release of Godot uses self-contained mode by default.
</description>
</method>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 2f7e68e463..522685bf87 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -396,6 +396,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
Item *ci = p_item_list;
Item *canvas_group_owner = nullptr;
+ bool skip_item = false;
state.last_item_index = 0;
@@ -464,6 +465,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ ci->canvas_group_owner->use_canvas_group = false;
items[item_count++] = ci->canvas_group_owner;
}
} else if (!backbuffer_cleared) {
@@ -478,9 +480,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
ci->canvas_group_owner = nullptr; //must be cleared
}
- if (!backbuffer_cleared && canvas_group_owner == nullptr && ci->canvas_group != nullptr && !backbuffer_copy) {
- texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
- backbuffer_cleared = true;
+ if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ skip_item = true;
}
if (ci == canvas_group_owner) {
@@ -498,6 +499,11 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
canvas_group_owner = nullptr;
// Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it.
backbuffer_cleared = false;
+
+ // Tell the renderer to paint this as a canvas group
+ ci->use_canvas_group = true;
+ } else {
+ ci->use_canvas_group = false;
}
if (backbuffer_copy) {
@@ -513,9 +519,9 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
backbuffer_copy = false;
- backbuffer_gen_mipmaps = false;
material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies.
material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps;
+ backbuffer_gen_mipmaps = false;
}
if (backbuffer_gen_mipmaps) {
@@ -526,14 +532,18 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
// just add all items for now
- items[item_count++] = ci;
+ if (skip_item) {
+ skip_item = false;
+ } else {
+ items[item_count++] = ci;
+ }
if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
if (update_skeletons) {
mesh_storage->update_mesh_instances();
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr);
//then reset
item_count = 0;
}
@@ -586,11 +596,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
- if (ci->canvas_group != nullptr) {
+ if (ci->use_canvas_group) {
if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- if (!p_to_backbuffer) {
- material = default_clip_children_material;
- }
+ material = default_clip_children_material;
} else {
if (material.is_null()) {
if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 1adba019ba..44c9dc714c 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -327,6 +327,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} else {
mesh->aabb.merge_with(p_surface.aabb);
}
+ mesh->skeleton_aabb_version = 0;
s->material = p_surface.material;
@@ -541,12 +542,12 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
Transform3D mtx;
- mtx.basis.rows[0].x = dataptr[0];
- mtx.basis.rows[1].x = dataptr[1];
+ mtx.basis.rows[0][0] = dataptr[0];
+ mtx.basis.rows[0][1] = dataptr[1];
mtx.origin.x = dataptr[3];
- mtx.basis.rows[0].y = dataptr[4];
- mtx.basis.rows[1].y = dataptr[5];
+ mtx.basis.rows[1][0] = dataptr[4];
+ mtx.basis.rows[1][1] = dataptr[5];
mtx.origin.y = dataptr[7];
AABB baabb = mtx.xform(skbones[j]);
@@ -1438,12 +1439,12 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p
t.origin.z = data[11];
} else {
- t.basis.rows[0].x = data[0];
- t.basis.rows[1].x = data[1];
+ t.basis.rows[0][0] = data[0];
+ t.basis.rows[0][1] = data[1];
t.origin.x = data[3];
- t.basis.rows[0].y = data[4];
- t.basis.rows[1].y = data[5];
+ t.basis.rows[1][0] = data[4];
+ t.basis.rows[1][1] = data[5];
t.origin.y = data[7];
}
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 91a746636a..2166d12d31 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -2530,7 +2530,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co
for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0.
- const uint8_t *read_ptr = read_ptr_mipmap + image_size * z / depth;
+ const uint8_t *read_ptr = read_ptr_mipmap + (image_size / depth) * z;
for (uint32_t y = 0; y < height; y += region_size) {
for (uint32_t x = 0; x < width; x += region_size) {
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 20a9e633a8..befe84f311 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -343,6 +343,9 @@ void ConnectDialog::_update_method_tree() {
si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")));
si_item->set_selectable(0, false);
+ if (si->get_script()->is_built_in()) {
+ si->get_script()->reload();
+ }
List<MethodInfo> methods;
si->get_method_list(&methods);
methods = _filter_method_list(methods, signal_info, search_string);
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index 389c16fd66..d5ba841801 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -117,14 +117,20 @@ EditorPaths::EditorPaths() {
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
+ Ref<DirAccess> d = DirAccess::create_for_path(exe_path);
+ if (d->file_exists(exe_path + "/._sc_")) {
+ self_contained = true;
+ self_contained_file = exe_path + "/._sc_";
+ } else if (d->file_exists(exe_path + "/_sc_")) {
+ self_contained = true;
+ self_contained_file = exe_path + "/_sc_";
+ }
// On macOS, look outside .app bundle, since .app bundle is read-only.
+ // Note: This will not work if Gatekeeper path randomization is active.
if (OS::get_singleton()->has_feature("macos") && exe_path.ends_with("MacOS") && exe_path.path_join("..").simplify_path().ends_with("Contents")) {
exe_path = exe_path.path_join("../../..").simplify_path();
- }
- {
- Ref<DirAccess> d = DirAccess::create_for_path(exe_path);
-
+ d = DirAccess::create_for_path(exe_path);
if (d->file_exists(exe_path + "/._sc_")) {
self_contained = true;
self_contained_file = exe_path + "/._sc_";
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 2ab15c1c2c..3e6b0ee07f 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -231,11 +231,11 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1,
return style;
}
-#ifdef MODULE_SVG_ENABLED
// See also `generate_icon()` in `scene/resources/default_theme.cpp`.
static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) {
Ref<Image> img = memnew(Image);
+#ifdef MODULE_SVG_ENABLED
// Upsample icon generation only if the editor scale isn't an integer multiplier.
// Generating upsampled icons is slower, and the benefit is hardly visible
// with integer editor scales.
@@ -246,13 +246,16 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float
if (p_saturation != 1.0) {
img->adjust_bcs(1.0, 1.0, p_saturation);
}
+#else
+ // If the SVG module is disabled, we can't really display the UI well, but at least we won't crash.
+ // 16 pixels is used as it's the most common base size for Godot icons.
+ img = Image::create_empty(16 * p_scale, 16 * p_scale, false, Image::FORMAT_RGBA8);
+#endif
return ImageTexture::create_from_image(img);
}
-#endif
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) {
-#ifdef MODULE_SVG_ENABLED
// Before we register the icons, we adjust their colors and saturation.
// Most icons follow the standard rules for color conversion to follow the editor
// theme's polarity (dark/light). We also adjust the saturation for most icons,
@@ -379,9 +382,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f
p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon);
}
}
-#else
- WARN_PRINT("SVG support disabled, editor icons won't be rendered.");
-#endif
}
Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
@@ -618,6 +618,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
regenerate_thumb_icons = !Math::is_equal_approx(prev_thumb_size, thumb_size);
}
+#ifndef MODULE_SVG_ENABLED
+ WARN_PRINT("SVG support disabled, editor icons won't be rendered.");
+#endif
+
if (keep_old_icons) {
for (int i = 0; i < editor_icons_count; i++) {
theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), p_theme->get_icon(editor_icons_names[i], SNAME("EditorIcons")));
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 5503645930..fac1ec3523 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -576,21 +576,21 @@ void LocalizationEditor::update_translations() {
translation_pot_list->clear();
root = translation_pot_list->create_item(nullptr);
translation_pot_list->set_hide_root(true);
- if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translations_pot_files")) {
- PackedStringArray pot_translations = GLOBAL_GET("internationalization/locale/translations_pot_files");
- for (int i = 0; i < pot_translations.size(); i++) {
- TreeItem *t = translation_pot_list->create_item(root);
- t->set_editable(0, false);
- t->set_text(0, pot_translations[i].replace_first("res://", ""));
- t->set_tooltip_text(0, pot_translations[i]);
- t->set_metadata(0, i);
- t->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0, false, TTR("Remove"));
- }
+ PackedStringArray pot_translations = GLOBAL_GET("internationalization/locale/translations_pot_files");
+ for (int i = 0; i < pot_translations.size(); i++) {
+ TreeItem *t = translation_pot_list->create_item(root);
+ t->set_editable(0, false);
+ t->set_text(0, pot_translations[i].replace_first("res://", ""));
+ t->set_tooltip_text(0, pot_translations[i]);
+ t->set_metadata(0, i);
+ t->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0, false, TTR("Remove"));
}
// New translation parser plugin might extend possible file extensions in POT generation.
_update_pot_file_extensions();
+ pot_generate_button->set_disabled(pot_translations.is_empty());
+
updating_translations = false;
}
@@ -726,9 +726,9 @@ LocalizationEditor::LocalizationEditor() {
addtr->connect("pressed", callable_mp(this, &LocalizationEditor::_pot_file_open));
thb->add_child(addtr);
- Button *generate = memnew(Button(TTR("Generate POT")));
- generate->connect("pressed", callable_mp(this, &LocalizationEditor::_pot_generate_open));
- thb->add_child(generate);
+ pot_generate_button = memnew(Button(TTR("Generate POT")));
+ pot_generate_button->connect("pressed", callable_mp(this, &LocalizationEditor::_pot_generate_open));
+ thb->add_child(pot_generate_button);
VBoxContainer *tmc = memnew(VBoxContainer);
tmc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/localization_editor.h b/editor/localization_editor.h
index 670ac5793b..b9a78a3c82 100644
--- a/editor/localization_editor.h
+++ b/editor/localization_editor.h
@@ -54,6 +54,7 @@ class LocalizationEditor : public VBoxContainer {
Tree *translation_pot_list = nullptr;
EditorFileDialog *pot_file_open_dialog = nullptr;
EditorFileDialog *pot_generate_dialog = nullptr;
+ Button *pot_generate_button = nullptr;
bool updating_translations = false;
String localization_changed;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 75a444c877..d610f1b937 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -4558,6 +4558,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
}
undo_redo->add_undo_method(viewport, "queue_redraw");
undo_redo->commit_action();
+ viewport->queue_redraw();
}
} break;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index c2d5885e43..4afbb87197 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -356,6 +356,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
return false;
}
+ original_point = handles[edit_handle];
original = get_handle_value(edit_handle);
original_transform = node->get_global_transform();
last_point = original;
@@ -572,6 +573,11 @@ void CollisionShape2DEditor::edit(Node *p_node) {
_get_current_shape_type();
} else {
+ if (pressed) {
+ set_handle(edit_handle, original_point);
+ pressed = false;
+ }
+
edit_handle = -1;
shape_type = -1;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 68fc0ddbdf..9c37b6cf9d 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -71,6 +71,7 @@ class CollisionShape2DEditor : public Control {
bool pressed;
Variant original;
Transform2D original_transform;
+ Vector2 original_point;
Point2 last_point;
Variant get_handle_value(int idx) const;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 5628a6a1be..5893f53f31 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1110,6 +1110,7 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
_edit.click_ray = _get_ray(p_point);
_edit.click_ray_pos = _get_ray_pos(p_point);
_edit.plane = TRANSFORM_VIEW;
+ spatial_editor->set_local_coords_enabled(false);
spatial_editor->update_transform_gizmo();
_edit.center = spatial_editor->get_gizmo_transform().origin;
@@ -2102,6 +2103,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (_edit.mode == TRANSFORM_NONE) {
+ if (_edit.gizmo.is_valid()) {
+ // Restore.
+ _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
+ _edit.gizmo = Ref<EditorNode3DGizmo>();
+ }
if (k->get_keycode() == Key::ESCAPE && !cursor.region_select) {
_clear_selected();
return;
@@ -3693,6 +3699,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
return;
}
+ bool show_gizmo = spatial_editor->is_gizmo_visible() && !_edit.instant;
for (int i = 0; i < 3; i++) {
Transform3D axis_angle;
if (xform.basis.get_column(i).normalized().dot(xform.basis.get_column((i + 1) % 3).normalized()) < 1.0) {
@@ -3701,15 +3708,15 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
axis_angle.basis.scale(scale);
axis_angle.origin = xform.origin;
RenderingServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
+ RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
RenderingServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
+ RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
+ RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
RenderingServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
+ RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
RenderingServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
+ RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
RenderingServer::get_singleton()->instance_set_transform(axis_gizmo_instance[i], xform);
}
@@ -4477,6 +4484,7 @@ void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) {
_compute_edit(_edit.mouse_pos);
_edit.instant = instant;
_edit.snap = spatial_editor->is_snap_enabled();
+ update_transform_gizmo_view();
}
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a584d357cd..49c815526b 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3586,7 +3586,72 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb
shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end);
return;
} else if (fpath.get_extension() == "tscn") {
+ Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ);
+ bool is_script_found = false;
+
+ // Starting from top of the tscn file.
+ int scr_start_line = 1;
+
+ String scr_header = "[sub_resource type=\"GDScript\" id=\"";
+ String scr_id = "";
+ String line = "";
+
+ int l = 0;
+
+ while (!f->eof_reached()) {
+ line = f->get_line();
+ l++;
+
+ if (!line.begins_with(scr_header)) {
+ continue;
+ }
+
+ // Found the end of the script.
+ scr_id = line.get_slice(scr_header, 1);
+ scr_id = scr_id.get_slice("\"", 0);
+
+ scr_start_line = l + 1;
+ int scr_line_count = 0;
+
+ do {
+ line = f->get_line();
+ l++;
+ String strline = line.strip_edges();
+
+ if (strline.ends_with("\"") && !strline.ends_with("\\\"")) {
+ // Found the end of script.
+ break;
+ }
+ scr_line_count++;
+
+ } while (!f->eof_reached());
+
+ if (line_number > scr_start_line + scr_line_count) {
+ // Find in another built-in GDScript.
+ continue;
+ }
+
+ // Real line number of the built-in script.
+ line_number = line_number - scr_start_line;
+
+ is_script_found = true;
+ break;
+ }
+
EditorNode::get_singleton()->load_scene(fpath);
+
+ if (is_script_found && !scr_id.is_empty()) {
+ Ref<Script> scr = ResourceLoader::load(fpath + "::" + scr_id, "Script");
+ if (scr.is_valid()) {
+ edit(scr);
+ ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor());
+
+ if (ste) {
+ ste->goto_line_selection(line_number, begin, end);
+ }
+ }
+ }
+
return;
} else {
Ref<Script> scr = res;
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 2519928ea3..c91d653692 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -2952,6 +2952,10 @@ void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
}
} break;
+ case Theme::DATA_TYPE_FONT_SIZE: {
+ ur->add_do_method(*edited_theme, "clear_font_size", p_item_name, edited_type);
+ ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
+ } break;
case Theme::DATA_TYPE_ICON: {
ur->add_do_method(*edited_theme, "clear_icon", p_item_name, edited_type);
if (edited_theme->has_icon(p_item_name, edited_type)) {
diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp
index f70a795683..9428d29088 100644
--- a/editor/pot_generator.cpp
+++ b/editor/pot_generator.cpp
@@ -55,7 +55,9 @@ void POTGenerator::_print_all_translation_strings() {
#endif
void POTGenerator::generate_pot(const String &p_file) {
- if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translations_pot_files")) {
+ Vector<String> files = GLOBAL_GET("internationalization/locale/translations_pot_files");
+
+ if (files.is_empty()) {
WARN_PRINT("No files selected for POT generation.");
return;
}
@@ -63,8 +65,6 @@ void POTGenerator::generate_pot(const String &p_file) {
// Clear all_translation_strings of the previous round.
all_translation_strings.clear();
- Vector<String> files = GLOBAL_GET("internationalization/locale/translations_pot_files");
-
// Collect all translatable strings according to files order in "POT Generation" setting.
for (int i = 0; i < files.size(); i++) {
Vector<String> msgids;
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index a1dfbac719..97b3a11adf 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -2268,6 +2268,10 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
if (line.contains("_unhandled_key_input(event: InputEventKey)")) {
line = line.replace("_unhandled_key_input(event: InputEventKey)", "_unhandled_key_input(event: InputEvent)");
}
+
+ if (line.contains("Engine.editor_hint")) {
+ line = line.replace("Engine.editor_hint", "Engine.is_editor_hint()");
+ }
}
void ProjectConverter3To4::process_csharp_line(String &line, const RegExContainer &reg_container) {
diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp
index eb63a244a6..61075492f9 100644
--- a/editor/renames_map_3_to_4.cpp
+++ b/editor/renames_map_3_to_4.cpp
@@ -1082,6 +1082,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "files_disabled", "file_disabled_color" }, // Theme
{ "folder_icon_modulate", "folder_icon_color" }, // Theme
{ "global_rate_scale", "playback_speed_scale" }, // AudioServer
+ { "global_translation", "global_position" }, // Node3D
{ "gravity_distance_scale", "gravity_point_unit_distance" }, // Area(2D/3D)
{ "gravity_vec", "gravity_direction" }, // Area(2D/3D)
{ "hint_tooltip", "tooltip_text" }, // Control
@@ -1095,6 +1096,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "margin_top", "offset_top" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "mid_height", "height" }, // CapsuleMesh
{ "neighbor_dist", "neighbor_distance" }, // NavigationAgent2D, NavigationAgent3D
+ { "octaves", "fractal_octaves" }, // OpenSimplexNoise -> FastNoiseLite
{ "offset_h", "drag_horizontal_offset" }, // Camera2D
{ "offset_v", "drag_vertical_offset" }, // Camera2D
{ "off_disabled", "unchecked_disabled" }, // Theme
@@ -1132,6 +1134,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
{ "tab_align", "tab_alignment" }, // TabContainer
{ "table_hseparation", "table_h_separation" }, // Theme
{ "table_vseparation", "table_v_separation" }, // Theme
+ { "tangent", "orthogonal" }, // Vector2
{ "toplevel", "top_level" }, // Node
{ "translation", "position" }, // Node3D
{ "unit_db", "volume_db" }, // AudioStreamPlayer3D
@@ -1188,6 +1191,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
{ "MarginTop", "OffsetTop" }, // Control -- Breaks NinePatchRect, StyleBox.
{ "MidHeight", "Height" }, // CapsuleMesh
{ "NeighborDist", "NeighborDistance" }, // NavigationAgent2D, NavigationAgent3D
+ { "Octaves", "FractalOctaves" }, // OpenSimplexNoise -> FastNoiseLite
{ "OffsetH", "DragHorizontalOffset" }, // Camera2D
{ "OffsetV", "DragVerticalOffset" }, // Camera2D
{ "OffDisabled", "UncheckedDisabled" }, // Theme
@@ -1195,6 +1199,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
{ "Oneshot", "OneShot" }, // AnimatedTexture
{ "OutOfRangeMode", "MaxPolyphony" }, // AudioStreamPlayer3D
{ "PauseMode", "ProcessMode" }, // Node
+ { "Perpendicular", "Orthogonal" }, // Vector2 - Only exists in C#
{ "PhysicalScancode", "PhysicalKeycode" }, // InputEventKey
{ "PopupExclusive", "Exclusive" }, // Window
{ "ProximityFadeEnable", "ProximityFadeEnabled" }, // Material
@@ -1225,6 +1230,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
{ "TabAlign", "TabAlignment" }, // TabContainer
{ "TableHseparation", "TableHSeparation" }, // Theme
{ "TableVseparation", "TableVSeparation" }, // Theme
+ { "Tangent", "Orthogonal" }, // Vector2
{ "Toplevel", "TopLevel" }, // Node
{ "Translation", "Position" }, // Node3D
{ "UnitDb", "VolumeDb" }, // AudioStreamPlayer3D
@@ -1476,6 +1482,7 @@ const char *RenamesMap3To4::class_renames[][2] = {
{ "CubeMesh", "BoxMesh" },
{ "CylinderShape", "CylinderShape3D" },
{ "DirectionalLight", "DirectionalLight3D" },
+ { "Directory", "DirAccess" },
{ "DynamicFont", "FontFile" },
{ "DynamicFontData", "FontFile" },
{ "EditorNavigationMeshGenerator", "NavigationMeshGenerator" },
@@ -1518,10 +1525,12 @@ const char *RenamesMap3To4::class_renames[][2] = {
{ "NavigationPolygonInstance", "NavigationRegion2D" },
{ "NavigationRegion", "NavigationRegion3D" },
{ "NavigationServer", "NavigationServer3D" },
+ { "NetworkedMultiplayerCustom", "MultiplayerPeerExtension" },
{ "NetworkedMultiplayerENet", "ENetMultiplayerPeer" },
{ "NetworkedMultiplayerPeer", "MultiplayerPeer" },
{ "Occluder", "OccluderInstance3D" },
{ "OmniLight", "OmniLight3D" },
+ { "OpenSimplexNoise", "FastNoiseLite" },
{ "PHashTranslation", "OptimizedTranslation" },
{ "PacketPeerGDNative", "PacketPeerExtension" },
{ "PanoramaSky", "Sky" },
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index c402b63f7b..00a3e41c2b 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1521,7 +1521,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
int error_count = 0;
do {
- if (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE)) {
+ if (is_at_end() || (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE))) {
break;
}
Node *statement = parse_statement();
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 7dcdc8e7cf..a736e36c6a 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -297,13 +297,14 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
}
return false;
}
-
- if (pipe.find("Blender ") != 0) {
+ int bl = pipe.find("Blender ");
+ if (bl == -1) {
if (r_err) {
*r_err = vformat(TTR("Unexpected --version output from Blender binary at: %s"), path);
}
return false;
}
+ pipe = pipe.substr(bl);
pipe = pipe.replace_first("Blender ", "");
int pp = pipe.find(".");
if (pp == -1) {
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index eea7520b05..894053c5de 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -14,7 +14,7 @@
<GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
- <GodotProjectDirBase64>$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
+ <GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
<BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath>
@@ -30,6 +30,13 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
+ <Target Condition=" $([MSBuild]::VersionLessThan($(MSBuildAssemblyVersion), '17.3')) " Name="GodotProjectDir" BeforeTargets="Build">
+ <Error Text="Cannot build from path containing '%23', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%23'))" /> <!-- # -->
+ <Error Text="Cannot build from path containing '%3B', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%3B'))" /> <!-- ; -->
+ <Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0A'))" /> <!-- \n -->
+ <Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0D'))" /> <!-- \r -->
+ </Target>
+
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index 2df838cfb8..3f569ebac3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -7,7 +7,7 @@
<PropertyGroup>
<!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
- <GodotProjectDirBase64>$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
+ <GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
<!-- For compiling GetGodotPropertyDefaultValues. -->
<DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
</PropertyGroup>
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index d08dedcfcb..bff7d04b55 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -66,23 +66,25 @@ String _get_mono_user_dir() {
if (EditorPaths::get_singleton()) {
return EditorPaths::get_singleton()->get_data_dir().path_join("mono");
} else {
- String settings_path;
+ String settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
-
- // On macOS, look outside .app bundle, since .app bundle is read-only.
- if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
- exe_dir = exe_dir.path_join("../../..").simplify_path();
- }
-
Ref<DirAccess> d = DirAccess::create_for_path(exe_dir);
-
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
// contain yourself
settings_path = exe_dir.path_join("editor_data");
- } else {
- settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
+ }
+
+ // On macOS, look outside .app bundle, since .app bundle is read-only.
+ // Note: This will not work if Gatekeeper path randomization is active.
+ if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
+ exe_dir = exe_dir.path_join("../../..").simplify_path();
+ d = DirAccess::create_for_path(exe_dir);
+ if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
+ // contain yourself
+ settings_path = exe_dir.path_join("editor_data");
+ }
}
return settings_path.path_join("mono");
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 2a29d86ec6..79c007ace6 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4322,7 +4322,7 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
elongation_count++;
}
}
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
space_count++;
}
}
@@ -4339,9 +4339,9 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
int count = delta_width_per_kashida / gl.advance;
int prev_count = gl.repeat;
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
- gl.repeat = MAX(count, 0);
+ gl.repeat = CLAMP(count, 0, 255);
} else {
- gl.repeat = MAX(count + 1, 1);
+ gl.repeat = CLAMP(count + 1, 1, 255);
}
justification_width += (gl.repeat - prev_count) * gl.advance;
}
@@ -4355,7 +4355,7 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
double old_adv = gl.advance;
double new_advance;
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
@@ -4791,6 +4791,19 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
gl.font_rid = sd_glyphs[i].font_rid;
gl.font_size = sd_glyphs[i].font_size;
gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | GRAPHEME_IS_SPACE;
+ // Mark virtual space after punctuation as punctuation to avoid justification at this point.
+ if (c_punct_size == 0) {
+ if (u_ispunct(c) && c != 0x005f) {
+ gl.flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ } else {
+ for (int j = 0; j < c_punct_size; j++) {
+ if (c_punct[j] == c) {
+ gl.flags |= GRAPHEME_IS_PUNCTUATION;
+ break;
+ }
+ }
+ }
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
gl.flags |= GRAPHEME_IS_RTL;
for (int j = sd_glyphs[i].count - 1; j >= 0; j--) {
@@ -5002,7 +5015,7 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap
}
}
}
- } else if ((sd_glyphs[i].flags & GRAPHEME_IS_SPACE) != GRAPHEME_IS_SPACE) {
+ } else if ((sd_glyphs[i].flags & GRAPHEME_IS_SPACE) != GRAPHEME_IS_SPACE && (sd_glyphs[i].flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
int count = sd_glyphs[i].count;
// Do not add extra spaces at the end of the line.
if (sd_glyphs[i].end == sd->end) {
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index e94b330d33..9a9dcb3a31 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3271,7 +3271,7 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
const Glyph &gl = sd->glyphs[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
space_count++;
}
}
@@ -3282,7 +3282,7 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
double old_adv = gl.advance;
gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
justification_width += (gl.advance - old_adv);
@@ -3381,7 +3381,7 @@ bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
if (c_punct_size == 0) {
- if (is_punct(c) && c != 0x005F) {
+ if (is_punct(c) && c != 0x005F && c != ' ') {
sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
}
} else {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index d1f1115aad..44d1757670 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -128,6 +128,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
#endif
case FEATURE_CLIPBOARD_PRIMARY:
case FEATURE_TEXT_TO_SPEECH:
+ case FEATURE_SCREEN_CAPTURE:
return true;
default: {
}
@@ -1169,6 +1170,29 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
return 96;
}
+Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const {
+ Point2i pos = p_position;
+
+ int number_of_screens = XScreenCount(x11_display);
+ for (int i = 0; i < number_of_screens; i++) {
+ Window root = XRootWindow(x11_display, i);
+ XWindowAttributes root_attrs;
+ XGetWindowAttributes(x11_display, root, &root_attrs);
+ if ((pos.x >= root_attrs.x) && (pos.x <= root_attrs.x + root_attrs.width) && (pos.y >= root_attrs.y) && (pos.y <= root_attrs.y + root_attrs.height)) {
+ XImage *image = XGetImage(x11_display, root, pos.x, pos.y, 1, 1, AllPlanes, XYPixmap);
+ if (image) {
+ XColor c;
+ c.pixel = XGetPixel(image, 0, 0);
+ XFree(image);
+ XQueryColor(x11_display, XDefaultColormap(x11_display, i), &c);
+ return Color(float(c.red) / 65535.0, float(c.green) / 65535.0, float(c.blue) / 65535.0, 1.0);
+ }
+ }
+ }
+
+ return Color();
+}
+
float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -2917,7 +2941,7 @@ BitField<MouseButtonMask> DisplayServerX11::_get_mouse_button_state(MouseButton
}
void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
- WindowData wd = windows[p_window];
+ WindowData &wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -4873,7 +4897,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
#ifdef GLES3_ENABLED
if (gl_manager) {
- visualInfo = gl_manager->get_vi(x11_display);
+ Error err;
+ visualInfo = gl_manager->get_vi(x11_display, err);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't acquire visual info from display.");
vi_selected = true;
}
#endif
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index dbe8a0ce2b..c98409253e 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -409,6 +409,7 @@ public:
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Color screen_get_pixel(const Point2i &p_position) const override;
#if defined(DBUS_ENABLED)
virtual void screen_set_keep_on(bool p_enable) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index 03ba95f475..ee767dfa80 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -83,8 +83,13 @@ int GLManager_X11::_find_or_create_display(Display *p_x11_display) {
d.context = memnew(GLManager_X11_Private);
d.context->glx_context = nullptr;
- //Error err = _create_context(d);
- _create_context(d);
+ Error err = _create_context(d);
+
+ if (err != OK) {
+ _displays.remove_at(new_display_id);
+ return -1;
+ }
+
return new_display_id;
}
@@ -191,8 +196,14 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) {
return OK;
}
-XVisualInfo GLManager_X11::get_vi(Display *p_display) {
- return _displays[_find_or_create_display(p_display)].x_vi;
+XVisualInfo GLManager_X11::get_vi(Display *p_display, Error &r_error) {
+ int display_id = _find_or_create_display(p_display);
+ if (display_id < 0) {
+ r_error = FAILED;
+ return XVisualInfo();
+ }
+ r_error = OK;
+ return _displays[display_id].x_vi;
}
Error GLManager_X11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
@@ -211,6 +222,10 @@ Error GLManager_X11::window_create(DisplayServer::WindowID p_window_id, ::Window
win.x11_window = p_window;
win.gldisplay_id = _find_or_create_display(p_display);
+ if (win.gldisplay_id == -1) {
+ return FAILED;
+ }
+
// the display could be invalid .. check NYI
GLDisplay &gl_display = _displays[win.gldisplay_id];
::Display *x11_display = gl_display.x11_display;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 0eb8ab64f4..0203dff679 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -114,7 +114,7 @@ private:
Error _create_context(GLDisplay &gl_display);
public:
- XVisualInfo get_vi(Display *p_display);
+ XVisualInfo get_vi(Display *p_display, Error &r_error);
Error window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height);
void window_destroy(DisplayServer::WindowID p_window_id);
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index cd46dab4f3..e3c1f17b8f 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -242,17 +242,17 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-lMoltenVK"])
mvk_found = False
- mkv_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"]
+ mvk_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"]
if env["vulkan_sdk_path"] != "":
- mkv_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
- mkv_list.insert(
+ mvk_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
+ mvk_list.insert(
0,
os.path.join(
os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
),
)
- for mvk_path in mkv_list:
+ for mvk_path in mvk_list:
if mvk_path and os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")):
mvk_found = True
print("MoltenVK found at: " + mvk_path)
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index fb9bcdfe56..f7c5b0b847 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -334,6 +334,7 @@ public:
virtual float screen_get_max_scale() const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Color screen_get_pixel(const Point2i &p_position) const override;
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index e8eb5b419b..eba69f8954 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -765,6 +765,7 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
case FEATURE_SWAP_BUFFERS:
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_EXTEND_TO_TITLE:
+ case FEATURE_SCREEN_CAPTURE:
return true;
default: {
}
@@ -2249,6 +2250,35 @@ Rect2i DisplayServerMacOS::screen_get_usable_rect(int p_screen) const {
return Rect2i();
}
+Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const {
+ Point2i position = p_position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value.
+ position.y *= -1;
+ position += _get_screens_origin();
+ position /= screen_get_max_scale();
+
+ for (NSScreen *screen in [NSScreen screens]) {
+ NSRect frame = [screen frame];
+ if (NSMouseInRect(NSMakePoint(position.x, position.y), frame, NO)) {
+ NSDictionary *screenDescription = [screen deviceDescription];
+ CGDirectDisplayID display_id = [[screenDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
+ CGImageRef image = CGDisplayCreateImageForRect(display_id, CGRectMake(position.x - frame.origin.x, frame.size.height - (position.y - frame.origin.y), 1, 1));
+ if (image) {
+ NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:image];
+ CGImageRelease(image);
+ NSColor *color = [bitmap colorAtX:0 y:0];
+ if (color) {
+ CGFloat components[4];
+ [color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
+ return Color(components[0], components[1], components[2], components[3]);
+ }
+ }
+ }
+ }
+ return Color();
+}
+
float DisplayServerMacOS::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 360e446de7..199386f9bf 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -90,6 +90,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
+ case FEATURE_SCREEN_CAPTURE:
return true;
default:
return false;
@@ -264,15 +265,15 @@ BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {
void DisplayServerWindows::clipboard_set(const String &p_text) {
_THREAD_SAFE_METHOD_
- if (!windows.has(last_focused_window)) {
- return; // No focused window?
+ if (!windows.has(MAIN_WINDOW_ID)) {
+ return;
}
// Convert LF line endings to CRLF in clipboard content.
// Otherwise, line endings won't be visible when pasted in other software.
String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // Avoid \r\r\n.
- if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {
ERR_FAIL_MSG("Unable to open clipboard.");
}
EmptyClipboard();
@@ -305,12 +306,12 @@ void DisplayServerWindows::clipboard_set(const String &p_text) {
String DisplayServerWindows::clipboard_get() const {
_THREAD_SAFE_METHOD_
- if (!windows.has(last_focused_window)) {
- return String(); // No focused window?
+ if (!windows.has(MAIN_WINDOW_ID)) {
+ return String();
}
String ret;
- if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ if (!OpenClipboard(windows[MAIN_WINDOW_ID].hWnd)) {
ERR_FAIL_V_MSG("", "Unable to open clipboard.");
}
@@ -631,6 +632,26 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const {
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
return data.dpi;
}
+
+Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {
+ Point2i pos = p_position + _get_screens_origin();
+
+ POINT p;
+ p.x = pos.x;
+ p.y = pos.y;
+ if (win81p_LogicalToPhysicalPointForPerMonitorDPI) {
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p);
+ }
+ HDC dc = GetDC(0);
+ COLORREF col = GetPixel(dc, p.x, p.y);
+ if (col != CLR_INVALID) {
+ return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0);
+ }
+ ReleaseDC(NULL, dc);
+
+ return Color();
+}
+
float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -4023,6 +4044,7 @@ GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColo
bool DisplayServerWindows::winink_available = false;
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
+LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr;
typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_DPI_UNAWARE = 0,
@@ -4151,10 +4173,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
// Note: Windows Ink API for pen input, available on Windows 8+ only.
+ // Note: DPI conversion API, available on Windows 8.1+ only.
HMODULE user32_lib = LoadLibraryW(L"user32.dll");
if (user32_lib) {
win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 0d2137d048..1b36b0951e 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -259,6 +259,7 @@ typedef struct tagPOINTER_PEN_INFO {
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
+typedef BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef struct {
BYTE bWidth; // Width, in pixels, of the image
@@ -305,6 +306,9 @@ class DisplayServerWindows : public DisplayServer {
static GetPointerTypePtr win8p_GetPointerType;
static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
+ // DPI conversion API
+ static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI;
+
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
String tablet_driver;
Vector<String> tablet_drivers;
@@ -524,6 +528,7 @@ public:
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Color screen_get_pixel(const Point2i &p_position) const override;
virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver
virtual bool screen_is_kept_on() const override;
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index c88eb033de..23855b2d8e 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -1207,7 +1207,7 @@ void CPUParticles3D::_update_particle_data_buffer() {
ptr[10] = t.basis.rows[2][2];
ptr[11] = t.origin.z;
} else {
- memset(ptr, 0, sizeof(Transform3D));
+ memset(ptr, 0, sizeof(float) * 12);
}
Color c = r[idx].color;
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index 10a7a4e78b..86301ee53f 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -117,8 +117,10 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {
mesh = p_mesh;
if (mesh.is_valid()) {
- mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
+ // If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback
+ // so do this before connecting _mesh_changed.
set_base(mesh->get_rid());
+ mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
_mesh_changed();
} else {
blend_shape_tracks.clear();
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index ee39327d4d..91c4fa3761 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2926,6 +2926,12 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_options.clear();
code_completion_base = string_to_complete;
+ /* Don't autocomplete setting numerical values. */
+ if (code_completion_base.is_numeric()) {
+ cancel_code_completion();
+ return;
+ }
+
Vector<ScriptLanguage::CodeCompletionOption> completion_options_casei;
Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr;
Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr_casei;
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index f10e1c2cd1..f9e96a44ed 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -58,6 +58,7 @@ void SubViewportContainer::set_stretch(bool p_enable) {
}
stretch = p_enable;
+ recalc_force_viewport_sizes();
update_minimum_size();
queue_sort();
queue_redraw();
@@ -75,10 +76,16 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) {
shrink = p_shrink;
+ recalc_force_viewport_sizes();
+ queue_redraw();
+}
+
+void SubViewportContainer::recalc_force_viewport_sizes() {
if (!stretch) {
return;
}
+ // If stretch is enabled, make sure that all child SubViwewports have the correct size.
for (int i = 0; i < get_child_count(); i++) {
SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
if (!c) {
@@ -87,8 +94,6 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) {
c->set_size_force(get_size() / shrink);
}
-
- queue_redraw();
}
int SubViewportContainer::get_stretch_shrink() const {
@@ -106,18 +111,7 @@ Vector<int> SubViewportContainer::get_allowed_size_flags_vertical() const {
void SubViewportContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_RESIZED: {
- if (!stretch) {
- return;
- }
-
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
- }
-
- c->set_size_force(get_size() / shrink);
- }
+ recalc_force_viewport_sizes();
} break;
case NOTIFICATION_ENTER_TREE:
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index d3236b0c4e..c1a74e5b98 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -58,6 +58,7 @@ public:
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
+ void recalc_force_viewport_sizes();
virtual Size2 get_minimum_size() const override;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 244e0d5b93..8cd57536bf 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -4281,6 +4281,11 @@ void SubViewport::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
+
+ SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
+ if (parent_svc) {
+ parent_svc->recalc_force_viewport_sizes();
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -4323,6 +4328,17 @@ void SubViewport::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
}
+void SubViewport::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "size") {
+ SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
+ if (parent_svc && parent_svc->is_stretch_enabled()) {
+ p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY;
+ } else {
+ p_property.usage = PROPERTY_USAGE_DEFAULT;
+ }
+ }
+}
+
SubViewport::SubViewport() {
RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 055fad5369..5213c0db01 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -784,6 +784,7 @@ public:
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
+ void _validate_property(PropertyInfo &p_property) const;
SubViewport();
~SubViewport();
};
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index cc606b5b88..2e1ba96d11 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -86,6 +86,10 @@ static Ref<ImageTexture> generate_icon(int p_index) {
ImageLoaderSVG img_loader;
Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>());
ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme.");
+#else
+ // If the SVG module is disabled, we can't really display the UI well, but at least we won't crash.
+ // 16 pixels is used as it's the most common base size for Godot icons.
+ img = Image::create_empty(16 * scale, 16 * scale, false, Image::FORMAT_RGBA8);
#endif
return ImageTexture::create_from_image(img);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 05be40c446..651bad1aa7 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1232,6 +1232,12 @@ Error ImageTexture3D::create(Image::Format p_format, int p_width, int p_height,
texture = tex;
}
+ format = p_format;
+ width = p_width;
+ height = p_height;
+ depth = p_depth;
+ mipmaps = p_mipmaps;
+
return OK;
}
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 2d65cea432..1e60094106 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -658,6 +658,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_touchscreen_available"), &DisplayServer::is_touchscreen_available, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_max_scale"), &DisplayServer::screen_get_max_scale);
ClassDB::bind_method(D_METHOD("screen_get_refresh_rate", "screen"), &DisplayServer::screen_get_refresh_rate, DEFVAL(SCREEN_OF_MAIN_WINDOW));
+ ClassDB::bind_method(D_METHOD("screen_get_pixel", "position"), &DisplayServer::screen_get_pixel);
ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
@@ -785,6 +786,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_CLIPBOARD_PRIMARY);
BIND_ENUM_CONSTANT(FEATURE_TEXT_TO_SPEECH);
BIND_ENUM_CONSTANT(FEATURE_EXTEND_TO_TITLE);
+ BIND_ENUM_CONSTANT(FEATURE_SCREEN_CAPTURE);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/servers/display_server.h b/servers/display_server.h
index aa30b25b65..fa37a694e6 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -124,6 +124,7 @@ public:
FEATURE_CLIPBOARD_PRIMARY,
FEATURE_TEXT_TO_SPEECH,
FEATURE_EXTEND_TO_TITLE,
+ FEATURE_SCREEN_CAPTURE,
};
virtual bool has_feature(Feature p_feature) const = 0;
@@ -275,6 +276,7 @@ public:
return scale;
}
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
+ virtual Color screen_get_pixel(const Point2i &p_position) const { return Color(); };
virtual bool is_touchscreen_available() const;
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index dc838047cc..c30e53c29e 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -321,6 +321,7 @@ public:
};
CanvasGroup *canvas_group = nullptr;
+ bool use_canvas_group = false;
int light_mask;
int z_final;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 78bdd3f541..f49d8be37a 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1110,11 +1110,9 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
- if (ci->canvas_group != nullptr) {
+ if (ci->use_canvas_group) {
if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
- if (!p_to_backbuffer) {
- material = default_clip_children_material;
- }
+ material = default_clip_children_material;
} else {
if (material.is_null()) {
if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
@@ -1381,6 +1379,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
bool backbuffer_gen_mipmaps = false;
Item *canvas_group_owner = nullptr;
+ bool skip_item = false;
bool update_skeletons = false;
bool time_used = false;
@@ -1453,6 +1452,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ ci->canvas_group_owner->use_canvas_group = false;
items[item_count++] = ci->canvas_group_owner;
}
} else if (!backbuffer_cleared) {
@@ -1467,9 +1467,8 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
ci->canvas_group_owner = nullptr; //must be cleared
}
- if (!backbuffer_cleared && canvas_group_owner == nullptr && ci->canvas_group != nullptr && !backbuffer_copy) {
- texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
- backbuffer_cleared = true;
+ if (canvas_group_owner == nullptr && ci->canvas_group != nullptr && ci->canvas_group->mode != RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ skip_item = true;
}
if (ci == canvas_group_owner) {
@@ -1488,6 +1487,11 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
canvas_group_owner = nullptr;
// Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it.
backbuffer_cleared = false;
+
+ // Tell the renderer to paint this as a canvas group
+ ci->use_canvas_group = true;
+ } else {
+ ci->use_canvas_group = false;
}
if (backbuffer_copy) {
@@ -1503,9 +1507,9 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
backbuffer_copy = false;
- backbuffer_gen_mipmaps = false;
material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies.
material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps;
+ backbuffer_gen_mipmaps = false;
}
if (backbuffer_gen_mipmaps) {
@@ -1515,7 +1519,11 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
material_screen_texture_mipmaps_cached = true;
}
- items[item_count++] = ci;
+ if (skip_item) {
+ skip_item = false;
+ } else {
+ items[item_count++] = ci;
+ }
if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
if (update_skeletons) {
@@ -1523,7 +1531,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
update_skeletons = false;
}
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, false);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, canvas_group_owner != nullptr);
//then reset
item_count = 0;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 96618c3352..b91e73c885 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -438,6 +438,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} else {
mesh->aabb.merge_with(p_surface.aabb);
}
+ mesh->skeleton_aabb_version = 0;
s->material = p_surface.material;
@@ -634,12 +635,12 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
Transform3D mtx;
- mtx.basis.rows[0].x = dataptr[0];
- mtx.basis.rows[1].x = dataptr[1];
+ mtx.basis.rows[0][0] = dataptr[0];
+ mtx.basis.rows[0][1] = dataptr[1];
mtx.origin.x = dataptr[3];
- mtx.basis.rows[0].y = dataptr[4];
- mtx.basis.rows[1].y = dataptr[5];
+ mtx.basis.rows[1][0] = dataptr[4];
+ mtx.basis.rows[1][1] = dataptr[5];
mtx.origin.y = dataptr[7];
AABB baabb = mtx.xform(skbones[j]);
@@ -1501,12 +1502,12 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p
t.origin.z = data[11];
} else {
- t.basis.rows[0].x = data[0];
- t.basis.rows[1].x = data[1];
+ t.basis.rows[0][0] = data[0];
+ t.basis.rows[0][1] = data[1];
t.origin.x = data[3];
- t.basis.rows[0].y = data[4];
- t.basis.rows[1].y = data[5];
+ t.basis.rows[1][0] = data[4];
+ t.basis.rows[1][1] = data[5];
t.origin.y = data[7];
}
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 813c1fa4ff..6a872da69e 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -653,6 +653,7 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) {
geom->geometry_instance->set_use_baked_light(instance->baked_light);
geom->geometry_instance->set_use_dynamic_gi(instance->dynamic_gi);
geom->geometry_instance->set_use_lightmap(RID(), instance->lightmap_uv_scale, instance->lightmap_slice_index);
+ geom->geometry_instance->set_instance_shader_uniforms_offset(instance->instance_allocated_shader_uniforms_offset);
geom->geometry_instance->set_cast_double_sided_shadows(instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED);
if (instance->lightmap_sh.size() == 9) {
geom->geometry_instance->set_lightmap_capture(instance->lightmap_sh.ptr());