summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/object/object.cpp1
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--editor/editor_help.cpp1
-rw-r--r--editor/engine_update_label.cpp89
-rw-r--r--editor/engine_update_label.h3
-rw-r--r--editor/import/3d/scene_import_settings.cpp2
-rw-r--r--editor/import/resource_importer_imagefont.cpp2
-rw-r--r--modules/gdscript/gdscript_editor.cpp8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java25
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp31
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
-rw-r--r--scene/2d/camera_2d.cpp16
-rw-r--r--scene/2d/camera_2d.h3
-rw-r--r--scene/gui/text_edit.cpp4
-rw-r--r--scene/main/node.cpp8
-rw-r--r--scene/resources/visual_shader_nodes.cpp52
-rw-r--r--scene/resources/visual_shader_nodes.h1
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp6
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp4
-rw-r--r--servers/rendering/renderer_viewport.cpp8
-rw-r--r--servers/rendering/storage/compositor_storage.cpp38
-rw-r--r--servers/rendering/storage/compositor_storage.h2
-rw-r--r--tests/scene/test_image_texture_3d.h101
-rw-r--r--tests/test_main.cpp1
25 files changed, 331 insertions, 83 deletions
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 303624e6d7..0383753f38 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -923,6 +923,7 @@ void Object::notification(int p_notification, bool p_reversed) {
}
String Object::to_string() {
+ // Keep this method in sync with `Node::to_string`.
if (script_instance) {
bool valid;
String ret = script_instance->to_string(&valid);
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 642bb76e75..1e7b63ecce 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -399,8 +399,8 @@
<param index="0" name="duration_ms" type="int" default="500" />
<param index="1" name="amplitude" type="float" default="-1.0" />
<description>
- [b]Note:[/b] While [code skip-lint]amplitude[/code] expects a value between 0 and 1, -1 does the default amplitude for the device.
Vibrate the handheld device for the specified duration in milliseconds.
+ [param amplitude] is the strength of the vibration, as a value between [code]0.0[/code] and [code]1.0[/code]. If set to [code]-1.0[/code], the default vibration strength of the device is used.
[b]Note:[/b] This method is implemented on Android, iOS, and Web. It has no effect on other platforms.
[b]Note:[/b] For Android, [method vibrate_handheld] requires enabling the [code]VIBRATE[/code] permission in the export preset. Otherwise, [method vibrate_handheld] will have no effect.
[b]Note:[/b] For iOS, specifying the duration is only supported in iOS 13 and later.
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index ec064a92d7..3dc8584096 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -3735,6 +3735,7 @@ EditorHelpBit::EditorHelpBit(const String &p_symbol) {
title = memnew(RichTextLabel);
title->set_theme_type_variation("EditorHelpBitTitle");
+ title->set_custom_minimum_size(Size2(512 * EDSCALE, 0)); // GH-93031. Set the minimum width even if `fit_content` is true.
title->set_fit_content(true);
title->set_selection_enabled(true);
//title->set_context_menu_enabled(true); // TODO: Fix opening context menu hides tooltip.
diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp
index 9984d6f02f..c9dfe1f521 100644
--- a/editor/engine_update_label.cpp
+++ b/editor/engine_update_label.cpp
@@ -63,7 +63,7 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
return;
}
- Array version_data;
+ Array version_array;
{
String s;
const uint8_t *r = p_body.ptr();
@@ -80,23 +80,22 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
_set_message(TTR("Received JSON data is not a valid version array."), theme_cache.error_color);
return;
}
- version_data = result;
+ version_array = result;
}
UpdateMode update_mode = UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode")));
bool stable_only = update_mode == UpdateMode::NEWEST_STABLE || update_mode == UpdateMode::NEWEST_PATCH;
- const Dictionary version_info = Engine::get_singleton()->get_version_info();
- int current_major = version_info["major"];
- int current_minor = version_info["minor"];
- int current_patch = version_info["patch"];
+ const Dictionary current_version_info = Engine::get_singleton()->get_version_info();
+ int current_major = current_version_info.get("major", 0);
+ int current_minor = current_version_info.get("minor", 0);
+ int current_patch = current_version_info.get("patch", 0);
- Dictionary found_version_info;
- for (const Variant &data_bit : version_data) {
- const Dictionary info = data_bit;
+ for (const Variant &data_bit : version_array) {
+ const Dictionary version_info = data_bit;
- const String version_string = info["name"];
- const PackedStringArray version_bits = version_string.split(".");
+ const String base_version_string = version_info.get("name", "");
+ const PackedStringArray version_bits = base_version_string.split(".");
if (version_bits.size() < 2) {
continue;
@@ -120,55 +119,45 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
continue;
}
+ const Array releases = version_info.get("releases", Array());
+ if (releases.is_empty()) {
+ continue;
+ }
+
+ const Dictionary newest_release = releases[0];
+ const String release_string = newest_release.get("name", "unknown");
+
+ int release_index;
+ VersionType release_type = _get_version_type(release_string, &release_index);
+
if (minor > current_minor || patch > current_patch) {
- String version_type = info["flavor"];
- if (stable_only && _get_version_type(version_type, nullptr) != VersionType::STABLE) {
+ if (stable_only && release_type != VersionType::STABLE) {
continue;
}
- found_version = version_string;
- found_version += "-" + version_type;
- break;
- } else if (minor == current_minor && patch == current_patch) {
- found_version_info = info;
- found_version = version_string;
+ available_newer_version = vformat("%s-%s", base_version_string, release_string);
break;
}
- }
-
- if (found_version_info.is_empty() && !found_version.is_empty()) {
- _set_status(UpdateStatus::UPDATE_AVAILABLE);
- _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
- return;
- } else if (found_version_info.is_empty() || stable_only) {
- _set_status(UpdateStatus::UP_TO_DATE);
- return;
- }
-
- int current_version_index;
- VersionType current_version_type = _get_version_type(version_info["status"], &current_version_index);
- const Array releases = found_version_info["releases"];
- for (const Variant &data_bit : version_data) {
- const Dictionary info = data_bit;
+ int current_version_index;
+ VersionType current_version_type = _get_version_type(current_version_info.get("status", "unknown"), &current_version_index);
- const String version_string = info["name"];
- int version_index;
- VersionType version_type = _get_version_type(version_string, &version_index);
-
- if (int(version_type) < int(current_version_type) || version_index > current_version_index) {
- found_version += "-" + version_string;
+ if (int(release_type) > int(current_version_type)) {
+ break;
+ }
- _set_status(UpdateStatus::UPDATE_AVAILABLE);
- _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
- return;
+ if (int(release_type) == int(current_version_type) && release_index < current_version_index) {
+ break;
}
+
+ available_newer_version = vformat("%s-%s", base_version_string, release_string);
+ break;
}
- if (current_version_index == DEV_VERSION) {
- // Since version index can't be determined and no strictly newer version exists, display a different status.
- _set_status(UpdateStatus::DEV);
- } else {
+ if (!available_newer_version.is_empty()) {
+ _set_status(UpdateStatus::UPDATE_AVAILABLE);
+ _set_message(vformat(TTR("Update available: %s."), available_newer_version), theme_cache.update_color);
+ } else if (available_newer_version.is_empty()) {
_set_status(UpdateStatus::UP_TO_DATE);
}
}
@@ -184,7 +173,7 @@ void EngineUpdateLabel::_set_message(const String &p_message, const Color &p_col
void EngineUpdateLabel::_set_status(UpdateStatus p_status) {
status = p_status;
- if (status == UpdateStatus::DEV || status == UpdateStatus::BUSY || status == UpdateStatus::UP_TO_DATE) {
+ if (status == UpdateStatus::BUSY || status == UpdateStatus::UP_TO_DATE) {
// Hide the label to prevent unnecessary distraction.
hide();
return;
@@ -306,7 +295,7 @@ void EngineUpdateLabel::pressed() {
} break;
case UpdateStatus::UPDATE_AVAILABLE: {
- OS::get_singleton()->shell_open("https://godotengine.org/download/archive/" + found_version);
+ OS::get_singleton()->shell_open("https://godotengine.org/download/archive/" + available_newer_version);
} break;
default: {
diff --git a/editor/engine_update_label.h b/editor/engine_update_label.h
index 3b852b8e53..5ebe271c8c 100644
--- a/editor/engine_update_label.h
+++ b/editor/engine_update_label.h
@@ -60,7 +60,6 @@ private:
enum class UpdateStatus {
NONE,
- DEV,
OFFLINE,
BUSY,
ERROR,
@@ -79,7 +78,7 @@ private:
UpdateStatus status = UpdateStatus::NONE;
bool checked_update = false;
- String found_version;
+ String available_newer_version;
bool _can_check_updates() const;
void _check_update();
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 129d0544c3..3d1eae26af 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -609,7 +609,7 @@ void SceneImportSettingsDialog::_update_camera() {
float rot_y = cam_rot_y;
float zoom = cam_zoom;
- if (selected_type == "Node" || selected_type.is_empty()) {
+ if (selected_type == "Node" || selected_type == "Animation" || selected_type.is_empty()) {
camera_aabb = contents_aabb;
} else {
if (mesh_preview->get_mesh().is_valid()) {
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
index 94e5dd0f12..0c87176a13 100644
--- a/editor/import/resource_importer_imagefont.cpp
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -229,6 +229,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
} else {
end = token.hex_to_int();
step = STEP_ADVANCE_BEGIN;
+ c--;
}
}
} break;
@@ -244,6 +245,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
} else {
end = token.to_int();
step = STEP_ADVANCE_BEGIN;
+ c--;
}
}
} break;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 11d4a4002c..fcabb7c4a8 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3571,9 +3571,13 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
if (base_type.class_type) {
- if (base_type.class_type->has_member(p_symbol)) {
+ String name = p_symbol;
+ if (name == "new") {
+ name = "_init";
+ }
+ if (base_type.class_type->has_member(name)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->get_member(p_symbol).get_line();
+ r_result.location = base_type.class_type->get_member(name).get_line();
r_result.class_path = base_type.script_path;
Error err = OK;
r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 43ae71f8e1..83e76e49c9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -228,16 +228,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
public boolean onGenericMotionEvent(MotionEvent event) {
lastSeenToolType = getEventToolType(event);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
- // The gesture detector has handled the event.
- return true;
- }
-
- if (godotGestureHandler.onMotionEvent(event)) {
- // The gesture handler has handled the event.
- return true;
- }
-
if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Check if the device exists
final int deviceId = event.getDeviceId();
@@ -273,11 +263,20 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
return true;
}
- } else {
- return handleMouseEvent(event);
+ return false;
}
- return false;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
+ // The gesture detector has handled the event.
+ return true;
+ }
+
+ if (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler has handled the event.
+ return true;
+ }
+
+ return handleMouseEvent(event);
}
public void initInputDevices() {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 0e4c723a87..a605e664ce 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -139,8 +139,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
#endif
case FEATURE_CLIPBOARD_PRIMARY:
case FEATURE_TEXT_TO_SPEECH:
- case FEATURE_SCREEN_CAPTURE:
return true;
+ case FEATURE_SCREEN_CAPTURE:
+ return !xwayland;
default: {
}
}
@@ -1494,9 +1495,20 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
return 96;
}
+int get_image_errorhandler(Display *dpy, XErrorEvent *ev) {
+ return 0;
+}
+
Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const {
Point2i pos = p_position;
+ if (xwayland) {
+ return Color();
+ }
+
+ int (*old_handler)(Display *, XErrorEvent *) = XSetErrorHandler(&get_image_errorhandler);
+
+ Color color;
int number_of_screens = XScreenCount(x11_display);
for (int i = 0; i < number_of_screens; i++) {
Window root = XRootWindow(x11_display, i);
@@ -1509,12 +1521,15 @@ Color DisplayServerX11::screen_get_pixel(const Point2i &p_position) const {
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);
+ color = Color(float(c.red) / 65535.0, float(c.green) / 65535.0, float(c.blue) / 65535.0, 1.0);
+ break;
}
}
}
- return Color();
+ XSetErrorHandler(old_handler);
+
+ return color;
}
Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
@@ -1533,6 +1548,12 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
ERR_FAIL_COND_V(p_screen < 0, Ref<Image>());
+ if (xwayland) {
+ return Ref<Image>();
+ }
+
+ int (*old_handler)(Display *, XErrorEvent *) = XSetErrorHandler(&get_image_errorhandler);
+
XImage *image = nullptr;
bool found = false;
@@ -1575,6 +1596,8 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const {
}
}
+ XSetErrorHandler(old_handler);
+
Ref<Image> img;
if (image) {
int width = image->width;
@@ -5815,6 +5838,8 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt
DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
KeyMappingX11::initialize();
+ xwayland = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").to_lower() == "wayland";
+
native_menu = memnew(NativeMenu);
context = p_context;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index f0b1811986..341ba5f079 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -330,6 +330,7 @@ class DisplayServerX11 : public DisplayServer {
bool xrandr_ext_ok = true;
bool xinerama_ext_ok = true;
bool xshaped_ext_ok = true;
+ bool xwayland = false;
struct Property {
unsigned char *data;
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 18ef2d8505..514c5e7a8f 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -74,6 +74,14 @@ void Camera2D::_update_scroll() {
}
}
+#ifdef TOOLS_ENABLED
+void Camera2D::_project_settings_changed() {
+ if (screen_drawing_enabled) {
+ queue_redraw();
+ }
+}
+#endif
+
void Camera2D::_update_process_callback() {
if (is_physics_interpolated_and_enabled()) {
set_process_internal(is_current());
@@ -267,6 +275,14 @@ void Camera2D::_ensure_update_interpolation_data() {
void Camera2D::_notification(int p_what) {
switch (p_what) {
+#ifdef TOOLS_ENABLED
+ case NOTIFICATION_READY: {
+ if (Engine::get_singleton()->is_editor_hint() && is_part_of_edited_scene()) {
+ ProjectSettings::get_singleton()->connect(SNAME("settings_changed"), callable_mp(this, &Camera2D::_project_settings_changed));
+ }
+ } break;
+#endif
+
case NOTIFICATION_INTERNAL_PROCESS: {
_update_scroll();
} break;
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index bf25267aa8..8754e35e88 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -88,6 +88,9 @@ protected:
bool _is_editing_in_editor() const;
void _update_process_callback();
void _update_scroll();
+#ifdef TOOLS_ENABLED
+ void _project_settings_changed();
+#endif
void _make_current(Object *p_which);
void _reset_just_exited() { just_exited_tree = false; }
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 2b2ea54dde..5a64dabb0e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3907,7 +3907,9 @@ void TextEdit::end_complex_operation() {
if (complex_operation_count > 0) {
return;
}
- ERR_FAIL_COND(undo_stack.is_empty());
+ if (undo_stack.is_empty()) {
+ return;
+ }
undo_stack.back()->get().end_carets = carets;
if (undo_stack.back()->get().chain_forward) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 884fc6de07..c6e5a4603e 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2583,6 +2583,7 @@ void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) c
}
String Node::to_string() {
+ // Keep this method in sync with `Object::to_string`.
ERR_THREAD_GUARD_V(String());
if (get_script_instance()) {
bool valid;
@@ -2591,7 +2592,12 @@ String Node::to_string() {
return ret;
}
}
-
+ if (_get_extension() && _get_extension()->to_string) {
+ String ret;
+ GDExtensionBool is_valid;
+ _get_extension()->to_string(_get_extension_instance(), &is_valid, &ret);
+ return ret;
+ }
return (get_name() ? String(get_name()) + ":" : "") + Object::to_string();
}
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index cb8719fbef..018b7be81c 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -6497,6 +6497,58 @@ bool VisualShaderNodeTextureParameter::is_show_prop_names() const {
return true;
}
+String VisualShaderNodeTextureParameter::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ if (texture_source != SOURCE_NONE) {
+ String texture_source_str;
+
+ switch (texture_source) {
+ case SOURCE_SCREEN: {
+ texture_source_str = "Screen";
+ } break;
+ case SOURCE_DEPTH: {
+ texture_source_str = "Depth";
+ } break;
+ case SOURCE_NORMAL_ROUGHNESS: {
+ texture_source_str = "NormalRoughness";
+ } break;
+ default:
+ break;
+ }
+
+ if (texture_type == TYPE_NORMAL_MAP || texture_type == TYPE_ANISOTROPY) {
+ String texture_type_str;
+
+ switch (texture_type) {
+ case TYPE_NORMAL_MAP: {
+ texture_type_str = "Normal Map";
+ } break;
+ case TYPE_ANISOTROPY: {
+ texture_type_str = "Anisotropic";
+ } break;
+ default:
+ break;
+ }
+ return vformat(RTR("'%s' type is incompatible with '%s' source."), texture_type_str, texture_source_str);
+ } else if (color_default != COLOR_DEFAULT_WHITE) {
+ String color_default_str;
+
+ switch (color_default) {
+ case COLOR_DEFAULT_BLACK: {
+ color_default_str = "Black";
+ } break;
+ case COLOR_DEFAULT_TRANSPARENT: {
+ color_default_str = "Transparent";
+ } break;
+ default:
+ break;
+ }
+ return vformat(RTR("'%s' default color is incompatible with '%s' source."), color_default_str, texture_source_str);
+ }
+ }
+
+ return "";
+}
+
HashMap<StringName, String> VisualShaderNodeTextureParameter::get_editable_properties_names() const {
HashMap<StringName, String> names;
names.insert("texture_type", RTR("Type"));
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index a23ae72def..7a37ffa0e0 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -2543,6 +2543,7 @@ public:
virtual HashMap<StringName, String> get_editable_properties_names() const override;
virtual bool is_show_prop_names() const override;
+ virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
Vector<StringName> get_editable_properties() const override;
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 42de831e7a..f872219425 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -272,8 +272,9 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
ci->repeat_times = repeat_times;
if (repeat_size.x || repeat_size.y) {
- rect.size += repeat_size * repeat_times / final_xform.get_scale();
- rect.position -= repeat_size * (repeat_times / 2);
+ Size2 scale = final_xform.get_scale();
+ rect.size += repeat_size * repeat_times / scale;
+ rect.position -= repeat_size / scale * (repeat_times / 2);
}
}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 9d3def1246..99622996d4 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1040,7 +1040,7 @@ void MeshStorage::update_mesh_instances() {
//process skeletons and blend shapes
uint64_t frame = RSG::rasterizer->get_frame_number();
- bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0) || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
while (dirty_mesh_instance_arrays.first()) {
@@ -1711,7 +1711,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index,
_multimesh_make_local(multimesh);
- bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0) || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0);
if (uses_motion_vectors) {
_multimesh_enable_motion_vectors(multimesh);
}
@@ -1934,7 +1934,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
ERR_FAIL_NULL(multimesh);
ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache));
- bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0);
+ bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0) || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0);
if (uses_motion_vectors) {
_multimesh_enable_motion_vectors(multimesh);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index f7b28e7a1e..314cbf9aa9 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -1306,7 +1306,7 @@ void ParticlesStorage::_particles_update_buffers(Particles *particles) {
userdata_count = particle_shader_data->userdata_count;
}
- bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0;
+ bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0 || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0);
bool index_draw_order = particles->draw_order == RS::ParticlesDrawOrder::PARTICLES_DRAW_ORDER_INDEX;
bool enable_motion_vectors = uses_motion_vectors && index_draw_order && !particles->instance_motion_vectors_enabled;
bool only_instances_changed = false;
@@ -1389,7 +1389,7 @@ void ParticlesStorage::update_particles() {
RENDER_TIMESTAMP("Update GPUParticles");
uint32_t frame = RSG::rasterizer->get_frame_number();
- bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0;
+ bool uses_motion_vectors = RSG::viewport->get_num_viewports_with_motion_vectors() > 0 || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0);
while (particle_update_list.first()) {
//use transform feedback to process particles
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 884f8adb8c..80c1f67d8a 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -971,7 +971,7 @@ void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int
}
bool RendererViewport::_viewport_requires_motion_vectors(Viewport *p_viewport) {
- return p_viewport->use_taa || p_viewport->scaling_3d_mode == RenderingServer::VIEWPORT_SCALING_3D_MODE_FSR2;
+ return p_viewport->use_taa || p_viewport->scaling_3d_mode == RenderingServer::VIEWPORT_SCALING_3D_MODE_FSR2 || p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS;
}
void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) {
@@ -1370,7 +1370,13 @@ void RendererViewport::viewport_set_debug_draw(RID p_viewport, RS::ViewportDebug
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
+ bool motion_vectors_before = _viewport_requires_motion_vectors(viewport);
viewport->debug_draw = p_draw;
+
+ bool motion_vectors_after = _viewport_requires_motion_vectors(viewport);
+ if (motion_vectors_before != motion_vectors_after) {
+ num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;
+ }
}
void RendererViewport::viewport_set_measure_render_time(RID p_viewport, bool p_enable) {
diff --git a/servers/rendering/storage/compositor_storage.cpp b/servers/rendering/storage/compositor_storage.cpp
index d9a70a093d..102efbeea1 100644
--- a/servers/rendering/storage/compositor_storage.cpp
+++ b/servers/rendering/storage/compositor_storage.cpp
@@ -53,7 +53,23 @@ void RendererCompositorStorage::compositor_effect_initialize(RID p_rid) {
}
void RendererCompositorStorage::compositor_effect_free(RID p_rid) {
- // TODO remove this RID from any compositor that uses it.
+ CompositorEffect *effect = compositor_effects_owner.get_or_null(p_rid);
+ ERR_FAIL_NULL(effect);
+
+ // Remove this RID from any compositor that uses it.
+ List<RID> compositor_rids;
+ compositor_owner.get_owned_list(&compositor_rids);
+ for (const RID &compositor_rid : compositor_rids) {
+ Compositor *compositor = compositor_owner.get_or_null(compositor_rid);
+ if (compositor) {
+ compositor->compositor_effects.erase(p_rid);
+ }
+ }
+
+ // Update motion vector count if needed.
+ if (effect->is_enabled && effect->flags.has_flag(RS::CompositorEffectFlags::COMPOSITOR_EFFECT_FLAG_NEEDS_MOTION_VECTORS)) {
+ num_compositor_effects_with_motion_vectors--;
+ }
compositor_effects_owner.free(p_rid);
}
@@ -70,6 +86,14 @@ void RendererCompositorStorage::compositor_effect_set_enabled(RID p_effect, bool
CompositorEffect *effect = compositor_effects_owner.get_or_null(p_effect);
ERR_FAIL_NULL(effect);
+ if (effect->is_enabled != p_enabled && effect->flags.has_flag(RS::CompositorEffectFlags::COMPOSITOR_EFFECT_FLAG_NEEDS_MOTION_VECTORS)) {
+ if (p_enabled) {
+ num_compositor_effects_with_motion_vectors++;
+ } else {
+ num_compositor_effects_with_motion_vectors--;
+ }
+ }
+
effect->is_enabled = p_enabled;
}
@@ -98,6 +122,18 @@ void RendererCompositorStorage::compositor_effect_set_flag(RID p_effect, RS::Com
CompositorEffect *effect = compositor_effects_owner.get_or_null(p_effect);
ERR_FAIL_NULL(effect);
+ if (effect->is_enabled && p_flag == RS::CompositorEffectFlags::COMPOSITOR_EFFECT_FLAG_NEEDS_MOTION_VECTORS) {
+ bool was_set = effect->flags.has_flag(RS::CompositorEffectFlags::COMPOSITOR_EFFECT_FLAG_NEEDS_MOTION_VECTORS);
+
+ if (was_set != p_set) {
+ if (p_set) {
+ num_compositor_effects_with_motion_vectors++;
+ } else {
+ num_compositor_effects_with_motion_vectors--;
+ }
+ }
+ }
+
if (p_set) {
effect->flags.set_flag(p_flag);
} else {
diff --git a/servers/rendering/storage/compositor_storage.h b/servers/rendering/storage/compositor_storage.h
index 0d6942e27f..d749743c6f 100644
--- a/servers/rendering/storage/compositor_storage.h
+++ b/servers/rendering/storage/compositor_storage.h
@@ -37,6 +37,7 @@
class RendererCompositorStorage {
private:
static RendererCompositorStorage *singleton;
+ int num_compositor_effects_with_motion_vectors = 0;
// Compositor effect
struct CompositorEffect {
@@ -59,6 +60,7 @@ private:
public:
static RendererCompositorStorage *get_singleton() { return singleton; }
+ int get_num_compositor_effects_with_motion_vectors() const { return num_compositor_effects_with_motion_vectors; }
RendererCompositorStorage();
virtual ~RendererCompositorStorage();
diff --git a/tests/scene/test_image_texture_3d.h b/tests/scene/test_image_texture_3d.h
new file mode 100644
index 0000000000..f2a7abcf69
--- /dev/null
+++ b/tests/scene/test_image_texture_3d.h
@@ -0,0 +1,101 @@
+/**************************************************************************/
+/* test_image_texture_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_IMAGE_TEXTURE_3D_H
+#define TEST_IMAGE_TEXTURE_3D_H
+
+#include "core/io/image.h"
+#include "scene/resources/image_texture.h"
+
+#include "tests/test_macros.h"
+#include "tests/test_utils.h"
+
+namespace TestImageTexture3D {
+
+// [SceneTree] in a test case name enables initializing a mock render server,
+// which ImageTexture3D is dependent on.
+TEST_CASE("[SceneTree][ImageTexture3D] Constructor") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_L8);
+ CHECK(image_texture_3d->get_width() == 1);
+ CHECK(image_texture_3d->get_height() == 1);
+ CHECK(image_texture_3d->get_depth() == 1);
+ CHECK(image_texture_3d->has_mipmaps() == false);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_format") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_L8);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_width") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_width() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_height") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_height() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_depth") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_depth() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] has_mipmaps") {
+ const Vector<Ref<Image>> images = { memnew(Image(8, 8, false, Image::FORMAT_RGBA8)), memnew(Image(8, 8, false, Image::FORMAT_RGBA8)) };
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->has_mipmaps() == false); // No mipmaps.
+ image_texture_3d->create(Image::FORMAT_RGBA8, 2, 2, 2, true, images);
+ CHECK(image_texture_3d->has_mipmaps() == true); // Mipmaps.
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] create") {
+ const Vector<Ref<Image>> images = { memnew(Image(8, 8, false, Image::FORMAT_RGBA8)), memnew(Image(8, 8, false, Image::FORMAT_RGBA8)) };
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->create(Image::FORMAT_RGBA8, 2, 2, 2, true, images) == OK); // Run create and check return value simultaneously.
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_RGBA8);
+ CHECK(image_texture_3d->get_width() == 2);
+ CHECK(image_texture_3d->get_height() == 2);
+ CHECK(image_texture_3d->get_depth() == 2);
+ CHECK(image_texture_3d->has_mipmaps() == true);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] set_path") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ String path = TestUtils::get_data_path("images/icon.png");
+ image_texture_3d->set_path(path, true);
+ CHECK(image_texture_3d->get_path() == path);
+}
+
+} //namespace TestImageTexture3D
+
+#endif // TEST_IMAGE_TEXTURE_3D_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 041231888b..3c875797a4 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -111,6 +111,7 @@
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_image_texture.h"
+#include "tests/scene/test_image_texture_3d.h"
#include "tests/scene/test_instance_placeholder.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_node_2d.h"