From e3aae1678a207710f764104f07078cd0341597f5 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Mon, 1 Mar 2021 12:44:39 +0000 Subject: [PATCH] Transform snapping moved to AdvancedSettings Transform snapping is made totally customizable, but is removed from project settings. It can now only be set from script etc using a new GDCLASS, AdvancedSettings. --- core/engine.cpp | 11 ++- core/engine.h | 14 +-- core/snappers.cpp | 74 +++++++++++++++ core/snappers.h | 108 ++++++++++++++++++++++ doc/classes/AdvancedSettings.xml | 62 +++++++++++++ doc/classes/ProjectSettings.xml | 8 -- main/main.cpp | 11 ++- scene/2d/animated_sprite.cpp | 7 +- scene/2d/sprite.cpp | 14 +-- scene/main/advanced_settings.cpp | 57 ++++++++++++ scene/main/advanced_settings.h | 62 +++++++++++++ scene/register_scene_types.cpp | 2 + servers/visual/visual_server_canvas.cpp | 15 ++- servers/visual/visual_server_canvas.h | 1 - servers/visual/visual_server_viewport.cpp | 17 ++-- 15 files changed, 418 insertions(+), 45 deletions(-) create mode 100644 core/snappers.cpp create mode 100644 core/snappers.h create mode 100644 doc/classes/AdvancedSettings.xml create mode 100644 scene/main/advanced_settings.cpp create mode 100644 scene/main/advanced_settings.h diff --git a/core/engine.cpp b/core/engine.cpp index d0f35b3b82ef..f327c1c00e7d 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -33,6 +33,7 @@ #include "core/authors.gen.h" #include "core/donors.gen.h" #include "core/license.gen.h" +#include "core/snappers.h" #include "core/version.h" #include "core/version_hash.gen.h" @@ -228,13 +229,21 @@ Engine::Engine() { _target_fps = 0; _time_scale = 1.0; _gpu_pixel_snap = false; - _snap_2d_transforms = false; _physics_frames = 0; _idle_frames = 0; _in_physics = false; _frame_ticks = 0; _frame_step = 0; editor_hint = false; + + _snappers = memnew(Snappers); +} + +Engine::~Engine() { + if (_snappers) { + memdelete(_snappers); + _snappers = nullptr; + } } Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr) : diff --git a/core/engine.h b/core/engine.h index 660699161988..3c20bdc405cb 100644 --- a/core/engine.h +++ b/core/engine.h @@ -36,6 +36,8 @@ #include "core/ustring.h" #include "core/vector.h" +class Snappers; + class Engine { public: @@ -58,15 +60,15 @@ class Engine { float _fps; int _target_fps; float _time_scale; - bool _gpu_pixel_snap; - bool _snap_2d_transforms; - bool _snap_2d_viewports; uint64_t _physics_frames; float _physics_interpolation_fraction; uint64_t _idle_frames; bool _in_physics; + bool _gpu_pixel_snap; + Snappers *_snappers; + List singletons; Map singleton_ptrs; @@ -109,8 +111,8 @@ class Engine { Object *get_singleton_object(const String &p_name) const; _FORCE_INLINE_ bool get_use_gpu_pixel_snap() const { return _gpu_pixel_snap; } - bool get_snap_2d_transforms() const { return _snap_2d_transforms; } - bool get_snap_2d_viewports() const { return _snap_2d_viewports; } + const Snappers &get_snappers() const { return *_snappers; } + Snappers &get_snappers() { return *_snappers; } #ifdef TOOLS_ENABLED _FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; } @@ -128,7 +130,7 @@ class Engine { String get_license_text() const; Engine(); - virtual ~Engine() {} + virtual ~Engine(); }; #endif // ENGINE_H diff --git a/core/snappers.cpp b/core/snappers.cpp new file mode 100644 index 000000000000..59bc0200656b --- /dev/null +++ b/core/snappers.cpp @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* snappers.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "snappers.h" +#include "core/project_settings.h" + +void Snappers::snap_read_item(Vector2 &r_pos) const { + if (snapper_canvas_item_read.is_enabled()) { + snapper_canvas_item_read.snap(r_pos); + } else if (_gpu_snap_enabled) { + r_pos = r_pos.floor(); + } +} + +void Snappers::set_stretch_mode(String p_mode) { + _stretch_mode_viewport = p_mode == "viewport"; +} + +void Snappers::set_transform_snap_2d(AdvancedSettings::Snap2DType p_type, AdvancedSettings::RoundMode p_mode_x, AdvancedSettings::RoundMode p_mode_y) { + switch (p_type) { + case AdvancedSettings::SNAP2D_TYPE_ITEM_PRE: { + snapper_canvas_item_pre.set_snap_modes(p_mode_x, p_mode_y); + } break; + case AdvancedSettings::SNAP2D_TYPE_ITEM_POST: { + snapper_canvas_item_post.set_snap_modes(p_mode_x, p_mode_y); + } break; + case AdvancedSettings::SNAP2D_TYPE_ITEM_READ: { + snapper_canvas_item_read.set_snap_modes(p_mode_x, p_mode_y); + } break; + case AdvancedSettings::SNAP2D_TYPE_VIEWPORT_PRE: { + snapper_viewport_pre.set_snap_modes(p_mode_x, p_mode_y); + } break; + case AdvancedSettings::SNAP2D_TYPE_VIEWPORT_POST: { + snapper_viewport_post.set_snap_modes(p_mode_x, p_mode_y); + } break; + case AdvancedSettings::SNAP2D_TYPE_VIEWPORT_PARENT_PRE: { + snapper_viewport_parent_pre.set_snap_modes(p_mode_x, p_mode_y); + } break; + default: { + } break; + } +} + +void Snappers::initialize(bool p_gpu_snap) { + _gpu_snap_enabled = p_gpu_snap; + _snap_transforms_enabled = false; +} diff --git a/core/snappers.h b/core/snappers.h new file mode 100644 index 000000000000..321bae8a9554 --- /dev/null +++ b/core/snappers.h @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* snappers.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 SNAPPERS_H +#define SNAPPERS_H + +#include "core/math/math_funcs.h" +#include "core/math/vector2.h" +#include "scene/main/advanced_settings.h" + +// generic class for handling 2d snapping +class Snapper2D { +public: + void snap(Vector2 &r_pos) const { + if (!_enabled) { + return; + } + + r_pos.x = snap_value(r_pos.x, _snap_mode_x); + r_pos.y = snap_value(r_pos.y, _snap_mode_y); + } + + void set_snap_modes(AdvancedSettings::RoundMode p_mode_x, AdvancedSettings::RoundMode p_mode_y) { + _snap_mode_x = p_mode_x; + _snap_mode_y = p_mode_y; + _enabled = !((_snap_mode_x == AdvancedSettings::ROUND_MODE_DISABLED) && (_snap_mode_y == AdvancedSettings::ROUND_MODE_DISABLED)); + } + + bool is_enabled() const { return _enabled; } + +private: + real_t snap_value(real_t p_value, AdvancedSettings::RoundMode p_mode) const { + switch (p_mode) { + default: + break; + case AdvancedSettings::ROUND_MODE_FLOOR: { + return Math::floor(p_value); + } break; + case AdvancedSettings::ROUND_MODE_CEILING: { + return Math::ceil(p_value); + } break; + case AdvancedSettings::ROUND_MODE_ROUND: { + return Math::round(p_value); + } break; + } + return p_value; + } + + bool _enabled = false; + AdvancedSettings::RoundMode _snap_mode_x = AdvancedSettings::ROUND_MODE_DISABLED; + AdvancedSettings::RoundMode _snap_mode_y = AdvancedSettings::ROUND_MODE_DISABLED; +}; + +// All the 2D snapping in one place. +// This is called from the various places it needs to be introduced, but the logic +// can be self contained here to make it easier to change / debug. +class Snappers { +public: + void initialize(bool p_gpu_snap); + void set_stretch_mode(String p_mode); + void set_transform_snap_2d(AdvancedSettings::Snap2DType p_type, AdvancedSettings::RoundMode p_mode_x, AdvancedSettings::RoundMode p_mode_y); + + // for positioning of sprites etc, not the main draw call + void snap_read_item(Vector2 &r_pos) const; + + Snapper2D snapper_canvas_item_pre; + Snapper2D snapper_canvas_item_post; + Snapper2D snapper_canvas_item_read; + + Snapper2D snapper_viewport_pre; + Snapper2D snapper_viewport_post; + Snapper2D snapper_viewport_parent_pre; + +private: + // local version + bool _gpu_snap_enabled = false; + bool _snap_transforms_enabled = false; + bool _stretch_mode_viewport = false; +}; + +#endif // SNAPPERS_H diff --git a/doc/classes/AdvancedSettings.xml b/doc/classes/AdvancedSettings.xml new file mode 100644 index 000000000000..2cee60d83de7 --- /dev/null +++ b/doc/classes/AdvancedSettings.xml @@ -0,0 +1,62 @@ + + + + Access to engine advanced settings. + + + + + + + + + + + + + Round mode applied to x coordinate. + + + Round mode applied to y coordinate. + Note that in Godot 2D, y increases down the screen, so using ceiling mode may give a better result than floor. + + + Gives total control over CPU transform snapping. + The engine defaults to all transform snapping disabled, but you can specify rounding modes for each part of the 2d pipeline. Items are items to be drawn, and viewports normally equate to cameras. + In general, for [code]viewport[/code] window stretch mode, pre transform snapping is most relevant, and for [code]2d[/code] window stretch mode, post transform snapping is most relevant. + + + + + + Snap the item local position when drawing. + + + Snap the item position after applying parent transform when drawing. + + + Snap the item local position when reading. + + + Snap canvas position. + + + Snap canvas position after applying global transform. + + + Snap canvas parent transform. + + + No snapping applied. + + + Snapping uses floor function (rounds down to the nearest whole number). + + + Snapping uses ceiling function (rounds up to the nearest whole number). + + + Snapping uses round function (rounds to the nearest whole number). + + + diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index af85d8cbf3d3..8602c0f5e90f 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1044,19 +1044,11 @@ Currently only available when [member rendering/batching/options/use_batching] is active. [b]Note:[/b] Antialiased software skinned polys are not supported, and will be rendered without antialiasing. - - If [code]true[/code], forces snapping of 2D viewports to the nearest whole coordinate. - Can reduce unwanted camera relative movement in pixel art styles. - If [code]true[/code], forces snapping of vertices to pixels in 2D rendering. May help in some pixel art styles. This snapping is performed on the GPU in the vertex shader. Consider using the project setting [member rendering/batching/precision/uv_contract] to prevent artifacts. - - If [code]true[/code], forces snapping of 2D object transforms to the nearest whole coordinate. - Can help prevent unwanted relative movement in pixel art styles. - When batching is on, this regularly prints a frame diagnosis log. Note that this will degrade performance. diff --git a/main/main.cpp b/main/main.cpp index 78257855943f..bd6d081ead85 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -45,6 +45,7 @@ #include "core/register_core_types.h" #include "core/script_debugger_local.h" #include "core/script_language.h" +#include "core/snappers.h" #include "core/translation.h" #include "core/version.h" #include "core/version_hash.gen.h" @@ -1125,8 +1126,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } Engine::get_singleton()->_gpu_pixel_snap = GLOBAL_DEF("rendering/2d/snapping/use_gpu_pixel_snap", false); - Engine::get_singleton()->_snap_2d_transforms = GLOBAL_DEF("rendering/2d/snapping/use_transform_snap", false); - Engine::get_singleton()->_snap_2d_viewports = GLOBAL_DEF("rendering/2d/snapping/use_camera_snap", false); + Engine::get_singleton()->_snappers->initialize(Engine::get_singleton()->get_use_gpu_pixel_snap()); + OS::get_singleton()->_keep_screen_on = GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); if (rtm == -1) { rtm = GLOBAL_DEF("rendering/threads/thread_model", OS::RENDER_THREAD_SAFE); @@ -1823,11 +1824,15 @@ bool Main::start() { Size2i stretch_size = Size2(GLOBAL_DEF("display/window/size/width", 0), GLOBAL_DEF("display/window/size/height", 0)); real_t stretch_shrink = GLOBAL_DEF("display/window/stretch/shrink", 1.0); + // the snappers need to know the stretch_mode + Engine::get_singleton()->_snappers->set_stretch_mode(stretch_mode); + SceneTree::StretchMode sml_sm = SceneTree::STRETCH_MODE_DISABLED; if (stretch_mode == "2d") sml_sm = SceneTree::STRETCH_MODE_2D; - else if (stretch_mode == "viewport") + else if (stretch_mode == "viewport") { sml_sm = SceneTree::STRETCH_MODE_VIEWPORT; + } SceneTree::StretchAspect sml_aspect = SceneTree::STRETCH_ASPECT_IGNORE; if (stretch_aspect == "keep") diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 6769dcc0cdee..3a8c6390eb60 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -31,6 +31,7 @@ #include "animated_sprite.h" #include "core/os/os.h" +#include "core/snappers.h" #include "scene/scene_string_names.h" #define NORMAL_SUFFIX "_normal" @@ -452,11 +453,7 @@ void AnimatedSprite::_notification(int p_what) { if (centered) ofs -= s / 2; - if (Engine::get_singleton()->get_snap_2d_transforms()) { - ofs = ofs.round(); - } else if (Engine::get_singleton()->get_use_gpu_pixel_snap()) { - ofs = ofs.floor(); - } + Engine::get_singleton()->get_snappers().snap_read_item(ofs); Rect2 dst_rect(ofs, s); if (hflip) diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index 7040a1741ccb..62fc27947faa 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -31,6 +31,7 @@ #include "sprite.h" #include "core/core_string_names.h" #include "core/os/os.h" +#include "core/snappers.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" @@ -100,11 +101,7 @@ void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_cli if (centered) dest_offset -= frame_size / 2; - if (Engine::get_singleton()->get_snap_2d_transforms()) { - dest_offset = dest_offset.round(); - } else if (Engine::get_singleton()->get_use_gpu_pixel_snap()) { - dest_offset = dest_offset.floor(); - } + Engine::get_singleton()->get_snappers().snap_read_item(dest_offset); r_dst_rect = Rect2(dest_offset, frame_size); @@ -381,11 +378,8 @@ Rect2 Sprite::get_rect() const { Point2 ofs = offset; if (centered) ofs -= Size2(s) / 2; - if (Engine::get_singleton()->get_snap_2d_transforms()) { - ofs = ofs.round(); - } else if (Engine::get_singleton()->get_use_gpu_pixel_snap()) { - ofs = ofs.floor(); - } + + Engine::get_singleton()->get_snappers().snap_read_item(ofs); if (s == Size2(0, 0)) s = Size2(1, 1); diff --git a/scene/main/advanced_settings.cpp b/scene/main/advanced_settings.cpp new file mode 100644 index 000000000000..374cd8634f13 --- /dev/null +++ b/scene/main/advanced_settings.cpp @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* advanced_settings.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "advanced_settings.h" +#include "core/engine.h" +#include "core/snappers.h" + +VARIANT_ENUM_CAST(AdvancedSettings::Snap2DType); +VARIANT_ENUM_CAST(AdvancedSettings::RoundMode); + +void AdvancedSettings::set_transform_snap_2d(Snap2DType p_type, RoundMode p_mode_x, RoundMode p_mode_y) { + Engine::get_singleton()->get_snappers().set_transform_snap_2d(p_type, p_mode_x, p_mode_y); +} + +void AdvancedSettings::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_transform_snap_2d", "snap_type", "mode_x", "mode_y"), &AdvancedSettings::set_transform_snap_2d); + + BIND_ENUM_CONSTANT(SNAP2D_TYPE_ITEM_PRE); + BIND_ENUM_CONSTANT(SNAP2D_TYPE_ITEM_POST); + BIND_ENUM_CONSTANT(SNAP2D_TYPE_ITEM_READ); + BIND_ENUM_CONSTANT(SNAP2D_TYPE_VIEWPORT_PRE); + BIND_ENUM_CONSTANT(SNAP2D_TYPE_VIEWPORT_POST); + BIND_ENUM_CONSTANT(SNAP2D_TYPE_VIEWPORT_PARENT_PRE); + + BIND_ENUM_CONSTANT(ROUND_MODE_DISABLED); + BIND_ENUM_CONSTANT(ROUND_MODE_FLOOR); + BIND_ENUM_CONSTANT(ROUND_MODE_CEILING); + BIND_ENUM_CONSTANT(ROUND_MODE_ROUND); +} diff --git a/scene/main/advanced_settings.h b/scene/main/advanced_settings.h new file mode 100644 index 000000000000..23bfb10d4f03 --- /dev/null +++ b/scene/main/advanced_settings.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* advanced_settings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 ADVANCED_SETTINGS_H +#define ADVANCED_SETTINGS_H + +#include "scene/main/node.h" + +class AdvancedSettings : public Reference { + GDCLASS(AdvancedSettings, Reference); + +public: + enum Snap2DType { + SNAP2D_TYPE_ITEM_PRE, + SNAP2D_TYPE_ITEM_POST, + SNAP2D_TYPE_ITEM_READ, + SNAP2D_TYPE_VIEWPORT_PRE, + SNAP2D_TYPE_VIEWPORT_POST, + SNAP2D_TYPE_VIEWPORT_PARENT_PRE, + }; + + enum RoundMode { + ROUND_MODE_DISABLED, + ROUND_MODE_FLOOR, + ROUND_MODE_CEILING, + ROUND_MODE_ROUND, + }; + + void set_transform_snap_2d(Snap2DType p_type, RoundMode p_mode_x, RoundMode p_mode_y); + +protected: + static void _bind_methods(); +}; + +#endif // ADVANCED_SETTINGS_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5c4ccbd386b5..b67b61ddd312 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -120,6 +120,7 @@ #include "scene/gui/tree.h" #include "scene/gui/video_player.h" #include "scene/gui/viewport_container.h" +#include "scene/main/advanced_settings.h" #include "scene/main/canvas_layer.h" #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" @@ -267,6 +268,7 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 060cd8b652e7..88a568ecd220 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "visual_server_canvas.h" +#include "core/snappers.h" #include "visual_server_globals.h" #include "visual_server_raster.h" #include "visual_server_viewport.h" @@ -99,11 +100,18 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor Rect2 rect = ci->get_rect(); Transform2D xform = ci->xform; - if (snap_2d_transforms) { - xform.elements[2] = xform.elements[2].round(); - } + + // opportunity to snap before AND after the p_transform is applied. + // for stretch_mode viewport, snapping before makes sense, + const Snappers &snappers = Engine::get_singleton()->get_snappers(); + snappers.snapper_canvas_item_pre.snap(xform.elements[2]); + xform = p_transform * xform; + // for stretch_mode 2d, we can snap after the scaling / zoom is applied, + // so we get sub-texel level snapping, which looks much better. + snappers.snapper_canvas_item_post.snap(xform.elements[2]); + Rect2 global_rect = xform.xform(rect); global_rect.position += p_clip_rect.position; @@ -1482,7 +1490,6 @@ VisualServerCanvas::VisualServerCanvas() { z_last_list = (RasterizerCanvas::Item **)memalloc(z_range * sizeof(RasterizerCanvas::Item *)); disable_scale = false; - snap_2d_transforms = Engine::get_singleton()->get_snap_2d_transforms(); } VisualServerCanvas::~VisualServerCanvas() { diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index 7503e9557256..19c3ff40b524 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -158,7 +158,6 @@ class VisualServerCanvas { RID_Owner canvas_light_owner; bool disable_scale; - bool snap_2d_transforms; private: void _render_canvas_item_tree(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights); diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index 0c64be8cbd78..21d3348f4eec 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -31,6 +31,7 @@ #include "visual_server_viewport.h" #include "core/project_settings.h" +#include "core/snappers.h" #include "visual_server_canvas.h" #include "visual_server_globals.h" #include "visual_server_scene.h" @@ -41,23 +42,25 @@ static Transform2D _canvas_get_transform(VisualServerViewport::Viewport *p_viewp float scale = 1.0; - bool snap = Engine::get_singleton()->get_snap_2d_viewports(); + const Snappers &snappers = Engine::get_singleton()->get_snappers(); if (p_viewport->canvas_map.has(p_canvas->parent)) { Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform; - if (snap) { - c_xform.elements[2] = c_xform.elements[2].round(); - } + + snappers.snapper_viewport_parent_pre.snap(c_xform.elements[2]); + xf = xf * c_xform; scale = p_canvas->parent_scale; } Transform2D c_xform = p_canvas_data->transform; - if (snap) { - c_xform.elements[2] = c_xform.elements[2].round(); - } + + // opportunity to snap pre and post the transform being applied.. + // pre may be better for stretch_mode viewport, post better for stretch_mode 2D + snappers.snapper_viewport_pre.snap(c_xform.elements[2]); xf = xf * c_xform; + snappers.snapper_viewport_post.snap(xf.elements[2]); if (scale != 1.0 && !VSG::canvas->disable_scale) { Vector2 pivot = p_vp_size * 0.5;