diff options
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"], ¤t_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"), ¤t_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" |