diff options
107 files changed, 6106 insertions, 2521 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index b4bbaaacb1..ace1ce3fec 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -26,7 +26,7 @@ jobs: - name: Configure dependencies run: | sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ - libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm + libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm # Upload cache on completion and check it out now - name: Load .scons_cache directory @@ -111,7 +111,7 @@ jobs: - name: Configure dependencies run: | sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ - libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm \ + libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm \ xvfb wget unzip # Upload cache on completion and check it out now @@ -204,7 +204,7 @@ jobs: - name: Configure dependencies run: | sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ - libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm + libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm # Upload cache on completion and check it out now - name: Load .scons_cache directory diff --git a/core/io/image.cpp b/core/io/image.cpp index 25d9eab3fe..3112dd217f 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -1985,6 +1985,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); + ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum."); int mm = 0; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -2007,6 +2008,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + "."); ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + "."); ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS)); + ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "Image format out of range, please see Image's Format enum."); int mm; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); @@ -2378,6 +2380,8 @@ Error Image::decompress() { } Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) { + ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode."); + ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source."); return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality); } @@ -2403,6 +2407,9 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); _image_compress_bptc_func(this, p_lossy_quality, p_channels); } break; + case COMPRESS_MAX: { + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } break; } return OK; @@ -2997,6 +3004,8 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) { + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot adjust_bcs in compressed or custom image formats."); + uint8_t *w = data.ptrw(); uint32_t pixel_size = get_format_pixel_size(format); uint32_t pixel_count = data.size() / pixel_size; diff --git a/core/io/image.h b/core/io/image.h index 060e54a308..8f1b251ac3 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -336,11 +336,13 @@ public: COMPRESS_ETC, COMPRESS_ETC2, COMPRESS_BPTC, + COMPRESS_MAX, }; enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_MAX, }; Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 4958b5ac6a..a2894bc1d3 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -362,6 +362,19 @@ public: return (intersections & 1); } + static bool is_segment_intersecting_polygon(const Vector2 &p_from, const Vector2 &p_to, const Vector<Vector2> &p_polygon) { + int c = p_polygon.size(); + const Vector2 *p = p_polygon.ptr(); + for (int i = 0; i < c; i++) { + const Vector2 &v1 = p[i]; + const Vector2 &v2 = p[(i + 1) % c]; + if (segment_intersects_segment(p_from, p_to, v1, v2, nullptr)) { + return true; + } + } + return false; + } + static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) { return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x); } diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index c9078a4de5..239e0bb2d9 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -85,6 +85,9 @@ <member name="icon" type="Texture2D" setter="set_button_icon" getter="get_button_icon"> Button's icon, if text is present the icon will be placed before the text. </member> + <member name="icon_align" type="int" setter="set_icon_align" getter="get_icon_align" enum="Button.TextAlign" default="0"> + Specifies if the icon should be aligned to the left, right, or center of a button. Uses the same [enum TextAlign] constants as the text alignment. If centered, text will draw on top of the icon. + </member> <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead. </member> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index 7feaaa2040..f98c31dc7f 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -486,6 +486,7 @@ </argument> <description> Stores any Variant value in the file. If [code]full_objects[/code] is [code]true[/code], encoding objects is allowed (and can potentially include code). + [b]Note:[/b] Not all properties are included. Only properties that are configured with the [constant PROPERTY_USAGE_STORAGE] flag set will be serialized. You can add a new usage flag to a property by overriding the [method Object._get_property_list] method in your class. You can also check how property usage is configured by calling [method Object._get_property_list]. See [enum PropertyUsageFlags] for the possible usage flags. </description> </method> </methods> diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index ee59f0c85a..0789ac9010 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -79,7 +79,7 @@ If [code]true[/code], wraps the text inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text. </member> <member name="clip_text" type="bool" setter="set_clip_text" getter="is_clipping_text" default="false"> - If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle. It also lets you scale the node down freely. + If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle and will clip text horizontally. </member> <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 21f3f1fcc0..f185a2bc57 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -426,7 +426,7 @@ Returns [code]true[/code] if the [NodePath] points to a valid node and its subname points to a valid resource, e.g. [code]Area2D/CollisionShape2D:shape[/code]. Properties with a non-[Resource] type (e.g. nodes or primitive math types) are not considered resources. </description> </method> - <method name="is_a_parent_of" qualifiers="const"> + <method name="is_ancestor_of" qualifiers="const"> <return type="bool"> </return> <argument index="0" name="node" type="Node"> diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 817ccd5160..0ba2e73ad4 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -20,6 +20,7 @@ @"/root/Main" # If your main scene's root node were named "Main". @"/root/MyAutoload" # If you have an autoloaded node or scene. [/codeblock] + [b]Note:[/b] In the editor, [NodePath] properties are automatically updated when moving, renaming or deleting a node in the scene tree, but they are never updated at runtime. </description> <tutorials> <link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 1d32a8b509..db01faced8 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -28,8 +28,8 @@ <member name="enable_shadows" type="bool" setter="set_enable_shadows" getter="are_shadows_enabled" default="false"> If [code]true[/code], computes shadows in the reflection probe. This makes the reflection probe slower to render; you may want to disable this if using the [constant UPDATE_ALWAYS] [member update_mode]. </member> - <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3(1, 1, 1)"> - The size of the reflection probe. The larger the extents the more space covered by the probe which will lower the perceived resolution. It is best to keep the extents only as large as you need them. + <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3(10, 10, 10)"> + The size of the reflection probe. The larger the extents, the more space covered by the probe, which will lower the perceived resolution. It is best to keep the extents only as large as you need them. </member> <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="1.0"> Defines the reflection intensity. Intensity modulates the strength of the reflection. diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml index 1f0f807a08..bb793024eb 100644 --- a/doc/classes/TileData.xml +++ b/doc/classes/TileData.xml @@ -7,7 +7,7 @@ <tutorials> </tutorials> <methods> - <method name="add_collision_shape"> + <method name="add_collision_polygon"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> @@ -15,27 +15,27 @@ <description> </description> </method> - <method name="get_collision_shape_one_way_margin" qualifiers="const"> + <method name="get_collision_polygon_one_way_margin" qualifiers="const"> <return type="float"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <description> </description> </method> - <method name="get_collision_shape_shape" qualifiers="const"> - <return type="Shape2D"> + <method name="get_collision_polygon_points" qualifiers="const"> + <return type="PackedVector2Array"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <description> </description> </method> - <method name="get_collision_shapes_count" qualifiers="const"> + <method name="get_collision_polygons_count" qualifiers="const"> <return type="int"> </return> <argument index="0" name="layer_id" type="int"> @@ -83,68 +83,68 @@ <description> </description> </method> - <method name="is_collision_shape_one_way" qualifiers="const"> + <method name="is_collision_polygon_one_way" qualifiers="const"> <return type="bool"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <description> </description> </method> - <method name="remove_collision_shape"> + <method name="remove_collision_polygon"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <description> </description> </method> - <method name="set_collision_shape_one_way"> + <method name="set_collision_polygon_one_way"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <argument index="2" name="one_way" type="bool"> </argument> <description> </description> </method> - <method name="set_collision_shape_one_way_margin"> + <method name="set_collision_polygon_one_way_margin"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> <argument index="2" name="one_way_margin" type="float"> </argument> <description> </description> </method> - <method name="set_collision_shape_shape"> + <method name="set_collision_polygon_points"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shape_index" type="int"> + <argument index="1" name="polygon_index" type="int"> </argument> - <argument index="2" name="shape" type="Shape2D"> + <argument index="2" name="polygon" type="PackedVector2Array"> </argument> <description> </description> </method> - <method name="set_collision_shapes_count"> + <method name="set_collision_polygons_count"> <return type="void"> </return> <argument index="0" name="layer_id" type="int"> </argument> - <argument index="1" name="shapes_count" type="int"> + <argument index="1" name="polygons_count" type="int"> </argument> <description> </description> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 7d8b589f78..55f232c004 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -289,8 +289,6 @@ </member> <member name="tile_size" type="Vector2i" setter="set_tile_size" getter="get_tile_size" default="Vector2i(16, 16)"> </member> - <member name="tile_skew" type="Vector2" setter="set_tile_skew" getter="get_tile_skew" default="Vector2(0, 0)"> - </member> <member name="uv_clipping" type="bool" setter="set_uv_clipping" getter="is_uv_clipping" default="false"> </member> <member name="y_sorting" type="bool" setter="set_y_sorting" getter="is_y_sorting" default="false"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 6a65d4ff7d..be770e6e03 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -232,7 +232,7 @@ Returns the last pressed button's index. </description> </method> - <method name="get_root"> + <method name="get_root" qualifiers="const"> <return type="TreeItem"> </return> <description> @@ -272,26 +272,26 @@ <description> </description> </method> - <method name="set_column_expand"> + <method name="set_column_custom_minimum_width"> <return type="void"> </return> <argument index="0" name="column" type="int"> </argument> - <argument index="1" name="expand" type="bool"> + <argument index="1" name="min_width" type="int"> </argument> <description> - If [code]true[/code], the column will have the "Expand" flag of [Control]. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. + Overrides the calculated minimum width of a column. It can be set to `0` to restore the default behavior. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. </description> </method> - <method name="set_column_min_width"> + <method name="set_column_expand"> <return type="void"> </return> <argument index="0" name="column" type="int"> </argument> - <argument index="1" name="min_width" type="int"> + <argument index="1" name="expand" type="bool"> </argument> <description> - Sets the minimum width of a column. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. + If [code]true[/code], the column will have the "Expand" flag of [Control]. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. </description> </method> <method name="set_column_title"> @@ -372,6 +372,12 @@ If [code]true[/code], the tree's root is hidden. </member> <member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" override="true" default="true" /> + <member name="scroll_horizontal_enabled" type="bool" setter="set_h_scroll_enabled" getter="is_h_scroll_enabled" default="true"> + If [code]true[/code], enables horizontal scrolling. + </member> + <member name="scroll_vertical_enabled" type="bool" setter="set_v_scroll_enabled" getter="is_v_scroll_enabled" default="true"> + If [code]true[/code], enables vertical scrolling. + </member> <member name="select_mode" type="int" setter="set_select_mode" getter="get_select_mode" enum="Tree.SelectMode" default="0"> Allows single or multiple selection. See the [enum SelectMode] constants. </member> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 0256d83fea..85c9caa101 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -192,7 +192,7 @@ Returns [code]true[/code] if [code]expand_right[/code] is set. </description> </method> - <method name="get_first_child"> + <method name="get_first_child" qualifiers="const"> <return type="TreeItem"> </return> <description> @@ -260,7 +260,7 @@ Returns the metadata value that was set for the given column using [method set_metadata]. </description> </method> - <method name="get_next"> + <method name="get_next" qualifiers="const"> <return type="TreeItem"> </return> <description> @@ -288,7 +288,7 @@ Returns OpenType feature [code]tag[/code] of the item's text. </description> </method> - <method name="get_parent"> + <method name="get_parent" qualifiers="const"> <return type="TreeItem"> </return> <description> @@ -391,7 +391,7 @@ Returns the given column's tooltip. </description> </method> - <method name="get_tree"> + <method name="get_tree" qualifiers="const"> <return type="Tree"> </return> <description> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 1539962803..6c4e590586 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -3524,7 +3524,10 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; - if (!is_multisample_first) { + if (is_multisample_first) { + texture_samples = p_attachments[attachment].samples; + is_multisample_first = false; + } else { ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples including the depth."); } diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 6ed43b5d31..9939611a83 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1013,11 +1013,11 @@ Error VulkanContext::_create_device() { return OK; } -Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { +Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { // Iterate over each queue to learn whether it supports presenting: VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32)); for (uint32_t i = 0; i < queue_family_count; i++) { - fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &supportsPresent[i]); + fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]); } // Search for a graphics and a present queue in the array of queue @@ -1091,10 +1091,10 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) { // Get the list of VkFormat's that are supported: uint32_t formatCount; - VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, nullptr); + VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); - err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, surfFormats); + err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats); if (err) { free(surfFormats); ERR_FAIL_V(ERR_CANT_CREATE); @@ -1169,9 +1169,6 @@ Error VulkanContext::_create_semaphores() { err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_acquired_semaphores[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -1201,6 +1198,19 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfa // is created. Error err = _initialize_queues(p_surface); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } else { + // make sure any of the surfaces supports present (validation layer complains if this is not done). + bool any_supports_present = false; + for (uint32_t i = 0; i < queue_family_count; i++) { + VkBool32 supports; + fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supports); + if (supports) { + any_supports_present = true; + break; + } + } + + ERR_FAIL_COND_V_MSG(!any_supports_present, ERR_CANT_CREATE, "Surface passed for sub-window creation does not support presenting"); } Window window; @@ -1210,6 +1220,17 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfa Error err = _update_swap_chain(&window); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + VkSemaphoreCreateInfo semaphoreCreateInfo = { + /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + /*pNext*/ nullptr, + /*flags*/ 0, + }; + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]); + ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); + } + windows[p_window_id] = window; return OK; } @@ -1249,6 +1270,10 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { ERR_FAIL_COND(!windows.has(p_window_id)); _clean_up_swap_chain(&windows[p_window_id]); + for (uint32_t i = 0; i < FRAME_LAG; i++) { + vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr); + } + vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); windows.erase(p_window_id); } @@ -1703,6 +1728,8 @@ Error VulkanContext::prepare_buffers() { for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { Window *w = &E->get(); + w->semaphore_acquired = false; + if (w->swapchain == VK_NULL_HANDLE) { continue; } @@ -1711,7 +1738,7 @@ Error VulkanContext::prepare_buffers() { // Get the index of the next available swapchain image: err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, - image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); + w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { // swapchain is out of date (e.g. the window was resized) and @@ -1724,8 +1751,10 @@ Error VulkanContext::prepare_buffers() { // presentation engine will still present the image correctly. print_verbose("Vulkan: Early suboptimal swapchain."); break; + } else if (err != VK_SUCCESS) { + ERR_BREAK(ERR_CANT_CREATE); } else { - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + w->semaphore_acquired = true; } } while (err != VK_SUCCESS); } @@ -1775,14 +1804,25 @@ Error VulkanContext::swap_buffers() { commands_to_submit = command_buffer_count; } + VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); + uint32_t semaphores_to_acquire_count = 0; + + for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) { + Window *w = &E->get(); + + if (w->semaphore_acquired) { + semaphores_to_acquire[semaphores_to_acquire_count++] = w->image_acquired_semaphores[frame_index]; + } + } + VkPipelineStageFlags pipe_stage_flags; VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.pWaitDstStageMask = &pipe_stage_flags; pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index]; + submit_info.waitSemaphoreCount = semaphores_to_acquire_count; + submit_info.pWaitSemaphores = semaphores_to_acquire; submit_info.commandBufferCount = commands_to_submit; submit_info.pCommandBuffers = commands_ptr; submit_info.signalSemaphoreCount = 1; @@ -2134,7 +2174,6 @@ VulkanContext::~VulkanContext() { if (device_initialized) { for (uint32_t i = 0; i < FRAME_LAG; i++) { vkDestroyFence(device, fences[i], nullptr); - vkDestroySemaphore(device, image_acquired_semaphores[i], nullptr); vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr); if (separate_present_queue) { vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 738ead4f96..8f1005d07f 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -70,7 +70,6 @@ private: }; VkInstance inst = VK_NULL_HANDLE; - VkSurfaceKHR surface = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDeviceProperties gpu_props; uint32_t queue_family_count = 0; @@ -101,7 +100,6 @@ private: VkQueue present_queue = VK_NULL_HANDLE; VkColorSpaceKHR color_space; VkFormat format; - VkSemaphore image_acquired_semaphores[FRAME_LAG]; VkSemaphore draw_complete_semaphores[FRAME_LAG]; VkSemaphore image_ownership_semaphores[FRAME_LAG]; int frame_index = 0; @@ -121,6 +119,8 @@ private: VkSwapchainKHR swapchain = VK_NULL_HANDLE; SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + VkSemaphore image_acquired_semaphores[FRAME_LAG]; + bool semaphore_acquired = false; uint32_t current_buffer = 0; int width = 0; int height = 0; @@ -208,7 +208,7 @@ private: Error _create_physical_device(); - Error _initialize_queues(VkSurfaceKHR surface); + Error _initialize_queues(VkSurfaceKHR p_surface); Error _create_device(); diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 0a0c3b1987..fe1401bdf9 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -1125,9 +1125,9 @@ ActionMapEditor::ActionMapEditor() { action_tree->set_column_title(0, TTR("Action")); action_tree->set_column_title(1, TTR("Deadzone")); action_tree->set_column_expand(1, false); - action_tree->set_column_min_width(1, 80 * EDSCALE); + action_tree->set_column_custom_minimum_width(1, 80 * EDSCALE); action_tree->set_column_expand(2, false); - action_tree->set_column_min_width(2, 50 * EDSCALE); + action_tree->set_column_custom_minimum_width(2, 50 * EDSCALE); action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited)); action_tree->connect("item_activated", callable_mp(this, &ActionMapEditor::_tree_item_activated)); action_tree->connect("button_pressed", callable_mp(this, &ActionMapEditor::_tree_button_pressed)); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 16f55312bb..3e4f382383 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -122,7 +122,7 @@ void FindReplaceBar::_unhandled_input(const Ref<InputEvent> &p_event) { } Control *focus_owner = get_focus_owner(); - if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_a_parent_of(focus_owner))) { + if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) { bool accepted = true; switch (k->get_keycode()) { diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp index 2d57dff69d..af83baeff8 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/editor/debugger/editor_network_profiler.cpp @@ -178,19 +178,19 @@ EditorNetworkProfiler::EditorNetworkProfiler() { counters_display->set_column_titles_visible(true); counters_display->set_column_title(0, TTR("Node")); counters_display->set_column_expand(0, true); - counters_display->set_column_min_width(0, 60 * EDSCALE); + counters_display->set_column_custom_minimum_width(0, 60 * EDSCALE); counters_display->set_column_title(1, TTR("Incoming RPC")); counters_display->set_column_expand(1, false); - counters_display->set_column_min_width(1, 120 * EDSCALE); + counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE); counters_display->set_column_title(2, TTR("Incoming RSET")); counters_display->set_column_expand(2, false); - counters_display->set_column_min_width(2, 120 * EDSCALE); + counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE); counters_display->set_column_title(3, TTR("Outgoing RPC")); counters_display->set_column_expand(3, false); - counters_display->set_column_min_width(3, 120 * EDSCALE); + counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE); counters_display->set_column_title(4, TTR("Outgoing RSET")); counters_display->set_column_expand(4, false); - counters_display->set_column_min_width(4, 120 * EDSCALE); + counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE); add_child(counters_display); frame_delay = memnew(Timer); diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 1bfef67e8c..449aaa42ff 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -631,13 +631,13 @@ EditorProfiler::EditorProfiler() { variables->set_column_titles_visible(true); variables->set_column_title(0, TTR("Name")); variables->set_column_expand(0, true); - variables->set_column_min_width(0, 60 * EDSCALE); + variables->set_column_custom_minimum_width(0, 60 * EDSCALE); variables->set_column_title(1, TTR("Time")); variables->set_column_expand(1, false); - variables->set_column_min_width(1, 100 * EDSCALE); + variables->set_column_custom_minimum_width(1, 100 * EDSCALE); variables->set_column_title(2, TTR("Calls")); variables->set_column_expand(2, false); - variables->set_column_min_width(2, 60 * EDSCALE); + variables->set_column_custom_minimum_width(2, 60 * EDSCALE); variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited)); graph = memnew(TextureRect); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index cc2e00d021..d3948dee97 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -773,13 +773,13 @@ EditorVisualProfiler::EditorVisualProfiler() { variables->set_column_titles_visible(true); variables->set_column_title(0, TTR("Name")); variables->set_column_expand(0, true); - variables->set_column_min_width(0, 60); + variables->set_column_custom_minimum_width(0, 60); variables->set_column_title(1, TTR("CPU")); variables->set_column_expand(1, false); - variables->set_column_min_width(1, 60 * EDSCALE); + variables->set_column_custom_minimum_width(1, 60 * EDSCALE); variables->set_column_title(2, TTR("GPU")); variables->set_column_expand(2, false); - variables->set_column_min_width(2, 60 * EDSCALE); + variables->set_column_custom_minimum_width(2, 60 * EDSCALE); variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected)); graph = memnew(TextureRect); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 7493cc2a8d..0d3fd8c7f6 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1643,7 +1643,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { error_tree->set_columns(2); error_tree->set_column_expand(0, false); - error_tree->set_column_min_width(0, 140); + error_tree->set_column_custom_minimum_width(0, 140); error_tree->set_column_expand(1, true); @@ -1731,13 +1731,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_tree->set_column_expand(0, true); vmem_tree->set_column_expand(1, false); vmem_tree->set_column_title(1, TTR("Type")); - vmem_tree->set_column_min_width(1, 100 * EDSCALE); + vmem_tree->set_column_custom_minimum_width(1, 100 * EDSCALE); vmem_tree->set_column_expand(2, false); vmem_tree->set_column_title(2, TTR("Format")); - vmem_tree->set_column_min_width(2, 150 * EDSCALE); + vmem_tree->set_column_custom_minimum_width(2, 150 * EDSCALE); vmem_tree->set_column_expand(3, false); vmem_tree->set_column_title(3, TTR("Usage")); - vmem_tree->set_column_min_width(3, 80 * EDSCALE); + vmem_tree->set_column_custom_minimum_width(3, 80 * EDSCALE); vmem_tree->set_hide_root(true); tabs->add_child(vmem_vb); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 7534b419fe..74a8ad9077 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -769,7 +769,7 @@ OrphanResourcesDialog::OrphanResourcesDialog() { files = memnew(Tree); files->set_columns(2); files->set_column_titles_visible(true); - files->set_column_min_width(1, 100); + files->set_column_custom_minimum_width(1, 100); files->set_column_expand(0, true); files->set_column_expand(1, false); files->set_column_title(0, TTR("Resource")); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index dbb23d8603..306a88047a 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -882,19 +882,19 @@ EditorAutoloadSettings::EditorAutoloadSettings() { tree->set_column_title(0, TTR("Name")); tree->set_column_expand(0, true); - tree->set_column_min_width(0, 100 * EDSCALE); + tree->set_column_custom_minimum_width(0, 100 * EDSCALE); tree->set_column_title(1, TTR("Path")); tree->set_column_expand(1, true); - tree->set_column_min_width(1, 100 * EDSCALE); + tree->set_column_custom_minimum_width(1, 100 * EDSCALE); tree->set_column_title(2, TTR("Global Variable")); tree->set_column_expand(2, false); // Reserve enough space for translations of "Global Variable" which may be longer. - tree->set_column_min_width(2, 150 * EDSCALE); + tree->set_column_custom_minimum_width(2, 150 * EDSCALE); tree->set_column_expand(3, false); - tree->set_column_min_width(3, 120 * EDSCALE); + tree->set_column_custom_minimum_width(3, 120 * EDSCALE); tree->connect("cell_selected", callable_mp(this, &EditorAutoloadSettings::_autoload_selected)); tree->connect("item_edited", callable_mp(this, &EditorAutoloadSettings::_autoload_edited)); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 7365e8fd7d..16db465a4a 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1909,7 +1909,7 @@ void FindBar::_unhandled_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->is_pressed() && (rich_text_label->has_focus() || is_a_parent_of(get_focus_owner()))) { + if (k->is_pressed() && (rich_text_label->has_focus() || is_ancestor_of(get_focus_owner()))) { bool accepted = true; switch (k->get_keycode()) { diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index b93ffa9321..57ddc64e95 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -239,7 +239,7 @@ EditorHelpSearch::EditorHelpSearch() { results_tree->set_column_title(0, TTR("Name")); results_tree->set_column_title(1, TTR("Member Type")); results_tree->set_column_expand(1, false); - results_tree->set_column_min_width(1, 150 * EDSCALE); + results_tree->set_column_custom_minimum_width(1, 150 * EDSCALE); results_tree->set_custom_minimum_size(Size2(0, 100) * EDSCALE); results_tree->set_hide_root(true); results_tree->set_select_mode(Tree::SELECT_ROW); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 437de97332..b3e90236a6 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -123,21 +123,20 @@ void EditorLog::_load_state() { Ref<ConfigFile> config; config.instantiate(); - Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); - - if (err == OK) { - const String section = "editor_log"; - for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { - E->get()->set_active(config->get_value(section, "log_filter_" + itos(E->key()), false)); - } + config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); - collapse = config->get_value(section, "collapse", false); - collapse_button->set_pressed(collapse); - bool show_search = config->get_value(section, "show_search", true); - search_box->set_visible(show_search); - show_search_button->set_pressed(show_search); + // Run the below code even if config->load returns an error, since we want the defaults to be set even if the file does not exist yet. + const String section = "editor_log"; + for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) { + E->get()->set_active(config->get_value(section, "log_filter_" + itos(E->key()), true)); } + collapse = config->get_value(section, "collapse", false); + collapse_button->set_pressed(collapse); + bool show_search = config->get_value(section, "show_search", true); + search_box->set_visible(show_search); + show_search_button->set_pressed(show_search); + is_loading_state = false; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b962ea6e3e..1bc04676b7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -709,6 +709,7 @@ void EditorNode::_notification(int p_what) { p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon("Instance", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_theme_icon("Godot", "EditorIcons")); @@ -2796,6 +2797,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case HELP_REPORT_A_BUG: { OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues"); } break; + case HELP_SUGGEST_A_FEATURE: { + OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme"); + } break; case HELP_SEND_DOCS_FEEDBACK: { OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues"); } break; @@ -6361,6 +6365,7 @@ EditorNode::EditorNode() { p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); + p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY); p->add_separator(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 9a135cfdd7..dcb6ad6e94 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -194,6 +194,7 @@ private: HELP_DOCS, HELP_QA, HELP_REPORT_A_BUG, + HELP_SUGGEST_A_FEATURE, HELP_SEND_DOCS_FEEDBACK, HELP_COMMUNITY, HELP_ABOUT, diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index 67ce1ade4d..62fbad7bcf 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -216,10 +216,10 @@ EditorPluginSettings::EditorPluginSettings() { plugin_list->set_column_expand(2, false); plugin_list->set_column_expand(3, false); plugin_list->set_column_expand(4, false); - plugin_list->set_column_min_width(1, 100 * EDSCALE); - plugin_list->set_column_min_width(2, 250 * EDSCALE); - plugin_list->set_column_min_width(3, 80 * EDSCALE); - plugin_list->set_column_min_width(4, 40 * EDSCALE); + plugin_list->set_column_custom_minimum_width(1, 100 * EDSCALE); + plugin_list->set_column_custom_minimum_width(2, 250 * EDSCALE); + plugin_list->set_column_custom_minimum_width(3, 80 * EDSCALE); + plugin_list->set_column_custom_minimum_width(4, 40 * EDSCALE); plugin_list->set_hide_root(true); plugin_list->connect("item_edited", callable_mp(this, &EditorPluginSettings::_plugin_activity_changed)); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index c28a61fecd..5ca596417b 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -491,6 +491,7 @@ void EditorPropertyEnum::update_property() { } void EditorPropertyEnum::setup(const Vector<String> &p_options) { + options->clear(); int64_t current_val = 0; for (int i = 0; i < p_options.size(); i++) { Vector<String> text_split = p_options[i].split(":"); @@ -2699,30 +2700,42 @@ void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) { } bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { + Control *editor = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide); + if (editor) { + add_property_editor(p_path, editor); + } + return false; +} + +void EditorInspectorDefaultPlugin::parse_end() { + //do none +} + +EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { double default_float_step = EDITOR_GET("interface/inspector/default_float_step"); switch (p_type) { // atomic types case Variant::NIL: { EditorPropertyNil *editor = memnew(EditorPropertyNil); - add_property_editor(p_path, editor); + return editor; } break; case Variant::BOOL: { EditorPropertyCheck *editor = memnew(EditorPropertyCheck); - add_property_editor(p_path, editor); + return editor; } break; case Variant::INT: { if (p_hint == PROPERTY_HINT_ENUM) { EditorPropertyEnum *editor = memnew(EditorPropertyEnum); Vector<String> options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_FLAGS) { EditorPropertyFlags *editor = memnew(EditorPropertyFlags); Vector<String> options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || @@ -2755,11 +2768,11 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } EditorPropertyLayers *editor = memnew(EditorPropertyLayers); editor->setup(lt); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_OBJECT_ID) { EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID); editor->setup(p_hint_text); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); @@ -2789,7 +2802,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, greater, lesser); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::FLOAT: { @@ -2809,7 +2822,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(full, flip); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyFloat *editor = memnew(EditorPropertyFloat); @@ -2841,7 +2854,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, hide_slider, exp_range, greater, lesser); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::STRING: { @@ -2849,14 +2862,14 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); Vector<String> options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) { EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_TYPE_STRING) { EditorPropertyClassName *editor = memnew(EditorPropertyClassName); editor->setup("Object", p_hint_text); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector<String> extensions = p_hint_text.split(","); bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; @@ -2867,7 +2880,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ if (save) { editor->set_save_mode(); } - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE || @@ -2905,14 +2918,14 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } editor->setup(type, p_hint_text); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); } - add_property_editor(p_path, editor); + return editor; } } break; @@ -2933,7 +2946,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR2I: { @@ -2948,7 +2961,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::RECT2: { @@ -2966,7 +2979,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide)); @@ -2980,7 +2993,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR3: { EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide)); @@ -2997,7 +3010,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR3I: { @@ -3013,7 +3026,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::TRANSFORM2D: { @@ -3031,7 +3044,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PLANE: { @@ -3049,7 +3062,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::QUATERNION: { EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion); @@ -3066,7 +3079,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::AABB: { EditorPropertyAABB *editor = memnew(EditorPropertyAABB); @@ -3083,7 +3096,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::BASIS: { EditorPropertyBasis *editor = memnew(EditorPropertyBasis); @@ -3100,7 +3113,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::TRANSFORM3D: { EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D); @@ -3117,7 +3130,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; @@ -3125,21 +3138,21 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case Variant::COLOR: { EditorPropertyColor *editor = memnew(EditorPropertyColor); editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA); - add_property_editor(p_path, editor); + return editor; } break; case Variant::STRING_NAME: { if (p_hint == PROPERTY_HINT_ENUM) { EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); Vector<String> options = p_hint_text.split(","); editor->setup(options, true); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); } editor->set_string_name(true); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::NODE_PATH: { @@ -3152,12 +3165,12 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ Vector<StringName> sn = Variant(types); //convert via variant editor->setup(NodePath(), sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT)); } - add_property_editor(p_path, editor); + return editor; } break; case Variant::RID: { EditorPropertyRID *editor = memnew(EditorPropertyRID); - add_property_editor(p_path, editor); + return editor; } break; case Variant::OBJECT: { EditorPropertyResource *editor = memnew(EditorPropertyResource); @@ -3176,70 +3189,66 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } - add_property_editor(p_path, editor); + return editor; } break; case Variant::DICTIONARY: { EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary); - add_property_editor(p_path, editor); + return editor; } break; case Variant::ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::ARRAY, p_hint_text); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_BYTE_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_BYTE_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_INT32_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_INT32_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_INT64_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_INT64_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_FLOAT32_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_FLOAT32_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_FLOAT64_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_FLOAT64_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_STRING_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_STRING_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_VECTOR2_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_VECTOR2_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_VECTOR3_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_VECTOR3_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_COLOR_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_COLOR_ARRAY); - add_property_editor(p_path, editor); + } break; default: { } } - return false; //can be overridden, although it will most likely be last anyway -} - -void EditorInspectorDefaultPlugin::parse_end() { - //do none + return nullptr; } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index dcde7dda3d..522f4eebf6 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -649,6 +649,8 @@ public: virtual void parse_begin(Object *p_object) override; virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false) override; virtual void parse_end() override; + + static EditorProperty *get_editor_for_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); }; #endif // EDITOR_PROPERTIES_H diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index c29b5d5906..cb8de09a9a 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -638,7 +638,7 @@ void FindInFilesPanel::set_with_replace(bool with_replace) { // Results show checkboxes on their left so they can be opted out _results_display->set_columns(2); _results_display->set_column_expand(0, false); - _results_display->set_column_min_width(0, 48 * EDSCALE); + _results_display->set_column_custom_minimum_width(0, 48 * EDSCALE); } else { // Results are single-cell items diff --git a/editor/icons/CenterView.svg b/editor/icons/CenterView.svg new file mode 100644 index 0000000000..c2a918fe81 --- /dev/null +++ b/editor/icons/CenterView.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="12" fill="none" r="2"/><g fill="#e0e0e0"><path d="m7.3333333 6c-.7386666 0-1.3333333.5946667-1.3333333 1.3333333v1.3333334c0 .7386663.5946667 1.3333333 1.3333333 1.3333333h1.3333334c.7386666 0 1.3333333-.594667 1.3333333-1.3333333v-1.3333334c0-.7386666-.5946667-1.3333333-1.3333333-1.3333333z" stroke-width=".666667"/><g stroke-width=".830398"><path d="m2.9918978 5.5000002c-.7478701.0001968-1.1170118.9026572-.5810669 1.4205652l.2441488.2424178h-2.15497967v1.6548989h2.15497967l-.2441488.242418c-.8159014.77992.3929613 1.9802119 1.178451 1.1700959l1.6667154-1.6548989c.3253369-.3231472.3253369-.8469488 0-1.170096l-1.6667154-1.6548989c-.1568986-.1601378-.3723426-.2504824-.5973507-.2504937z"/><path d="m13.008102 10.5c.74787-.000197 1.117012-.9026571.581067-1.4205651l-.244149-.242418h2.15498v-1.6548988h-2.15498l.244149-.2424179c.815901-.7799207-.392961-1.9802122-1.178451-1.1700961l-1.666715 1.6548987c-.325337.3231474-.325337.846949 0 1.1700961l1.666715 1.6548991c.156899.160138.372343.250482.597351.250494z"/><path d="m5.5000001 13.008102c.000197.74787.902657 1.117012 1.420565.581067l.242418-.244149v2.15498h1.654899v-2.15498l.242418.244149c.77992.815901 1.9802119-.392961 1.1700959-1.178451l-1.6548989-1.666715c-.323147-.325337-.846949-.325337-1.170096 0l-1.654899 1.666715c-.160138.156899-.250482.372343-.250494.597351z"/><path d="m10.5 2.9918983c-.000197-.7478701-.9026571-1.1170121-1.4205651-.581067l-.242418.244149v-2.1549801h-1.6548989v2.1549801l-.242418-.2441491c-.77992-.815901-1.9802121.3929611-1.170096 1.1784512l1.654899 1.6667148c.3231469.325337.8469489.325337 1.1700959 0l1.6548991-1.6667148c.160138-.156899.250482-.3723431.250494-.597351z"/></g></g></svg> diff --git a/editor/icons/RectangleAddRemove.svg b/editor/icons/RectangleAddRemove.svg deleted file mode 100644 index 87e2155a0d..0000000000 --- a/editor/icons/RectangleAddRemove.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2c-.5522619.0000552-.9999448.4477381-1 1v10c.0000552.552262.4477381.999945 1 1h6v-2h-5v-8h10v1h2v-2c-.000055-.5522619-.447738-.9999448-1-1zm9.25 4v2.25h-2.25v1.5h2.25v2.25h1.5v-2.25h2.25v-1.5h-2.25v-2.25zm-2.25 7.5v1.5h6v-1.5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/TileChecked.svg b/editor/icons/TileChecked.svg new file mode 100644 index 0000000000..33b99a0e4c --- /dev/null +++ b/editor/icons/TileChecked.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#808080" stroke-width="1.16667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" fill="#fff" stroke-width="1.06023"/></svg> diff --git a/editor/icons/TileUnchecked.svg b/editor/icons/TileUnchecked.svg new file mode 100644 index 0000000000..cd8db4ee19 --- /dev/null +++ b/editor/icons/TileUnchecked.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#808080" stroke-width="1.16667"/></svg> diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index cb9fd1f28e..f9f47ec4f4 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -1163,13 +1163,13 @@ SceneImportSettings::SceneImportSettings() { external_path_tree->set_columns(3); external_path_tree->set_column_titles_visible(true); external_path_tree->set_column_expand(0, true); - external_path_tree->set_column_min_width(0, 100 * EDSCALE); + external_path_tree->set_column_custom_minimum_width(0, 100 * EDSCALE); external_path_tree->set_column_title(0, TTR("Resource")); external_path_tree->set_column_expand(1, true); - external_path_tree->set_column_min_width(1, 100 * EDSCALE); + external_path_tree->set_column_custom_minimum_width(1, 100 * EDSCALE); external_path_tree->set_column_title(1, TTR("Path")); external_path_tree->set_column_expand(2, false); - external_path_tree->set_column_min_width(2, 200 * EDSCALE); + external_path_tree->set_column_custom_minimum_width(2, 200 * EDSCALE); external_path_tree->set_column_title(2, TTR("Status")); save_path = memnew(EditorFileDialog); save_path->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR); diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 161f1dde0d..91a15f1131 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -729,7 +729,7 @@ LocalizationEditor::LocalizationEditor() { translation_remap_options->set_column_titles_visible(true); translation_remap_options->set_column_expand(0, true); translation_remap_options->set_column_expand(1, false); - translation_remap_options->set_column_min_width(1, 200); + translation_remap_options->set_column_custom_minimum_width(1, 200); translation_remap_options->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_changed)); translation_remap_options->connect("button_pressed", callable_mp(this, &LocalizationEditor::_translation_res_option_delete)); tmc->add_child(translation_remap_options); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index e31c98db92..48d7cfdee2 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -569,8 +569,8 @@ void AnimationPlayerEditor::_animation_blend() { blend_editor.dialog->popup_centered(Size2(400, 400) * EDSCALE); blend_editor.tree->set_hide_root(true); - blend_editor.tree->set_column_min_width(0, 10); - blend_editor.tree->set_column_min_width(1, 3); + blend_editor.tree->set_column_custom_minimum_width(0, 10); + blend_editor.tree->set_column_custom_minimum_width(1, 3); List<StringName> anims; player->get_animation_list(&anims); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 24d835887e..033a3e4fab 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1241,7 +1241,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid() && !p_already_accepted) { - // If control key pressed, then zoom instead of pan + // If ctrl key pressed, then zoom instead of pan. if (pan_gesture->is_ctrl_pressed()) { const float factor = pan_gesture->get_delta().y; diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index c3b121d242..59c0d81ae7 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -6373,7 +6373,7 @@ void Node3DEditor::_request_gizmo(Object *p_obj) { if (!sp) { return; } - if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_a_parent_of(sp)))) { + if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_ancestor_of(sp)))) { Ref<EditorNode3DGizmo> seg; for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { @@ -6447,7 +6447,7 @@ void Node3DEditor::_toggle_maximize_view(Object *p_viewport) { } void Node3DEditor::_node_added(Node *p_node) { - if (EditorNode::get_singleton()->get_scene_root()->is_a_parent_of(p_node)) { + if (EditorNode::get_singleton()->get_scene_root()->is_ancestor_of(p_node)) { if (Object::cast_to<WorldEnvironment>(p_node)) { world_env_count++; if (world_env_count == 1) { @@ -6463,7 +6463,7 @@ void Node3DEditor::_node_added(Node *p_node) { } void Node3DEditor::_node_removed(Node *p_node) { - if (EditorNode::get_singleton()->get_scene_root()->is_a_parent_of(p_node)) { + if (EditorNode::get_singleton()->get_scene_root()->is_ancestor_of(p_node)) { if (Object::cast_to<WorldEnvironment>(p_node)) { world_env_count--; if (world_env_count == 0) { diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index b8b2c6d343..a7c11f8521 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -367,8 +367,8 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { tree = memnew(Tree); tree->connect("button_pressed", callable_mp(this, &ResourcePreloaderEditor::_cell_button_pressed)); tree->set_columns(2); - tree->set_column_min_width(0, 2); - tree->set_column_min_width(1, 3); + tree->set_column_custom_minimum_width(0, 2); + tree->set_column_custom_minimum_width(1, 3); tree->set_column_expand(0, true); tree->set_column_expand(1, true); tree->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 4ab5887cd7..99d7267eac 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -932,9 +932,9 @@ ThemeItemImportTree::ThemeItemImportTree() { import_items_tree->set_column_expand(0, true); import_items_tree->set_column_expand(IMPORT_ITEM, false); import_items_tree->set_column_expand(IMPORT_ITEM_DATA, false); - import_items_tree->set_column_min_width(0, 160 * EDSCALE); - import_items_tree->set_column_min_width(IMPORT_ITEM, 80 * EDSCALE); - import_items_tree->set_column_min_width(IMPORT_ITEM_DATA, 80 * EDSCALE); + import_items_tree->set_column_custom_minimum_width(0, 160 * EDSCALE); + import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM, 80 * EDSCALE); + import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM_DATA, 80 * EDSCALE); ScrollContainer *import_bulk_sc = memnew(ScrollContainer); import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 78f181e321..374a255df3 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -33,7 +33,6 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "scene/gui/box_container.h" -#include "scene/gui/center_container.h" #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/gui/texture_rect.h" @@ -44,21 +43,41 @@ void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) { bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL); - Ref<InputEventMouseButton> b = p_event; - if (b.is_valid()) { - if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + drag_type = DRAG_TYPE_NONE; + if (ctrl && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { // Zoom out zoom_widget->set_zoom_by_increments(-2); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); - _update_zoom(zoom_widget->get_zoom(), true); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); + _update_zoom_and_panning(true); accept_event(); } - if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { + if (ctrl && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { // Zoom in zoom_widget->set_zoom_by_increments(2); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); - _update_zoom(zoom_widget->get_zoom(), true); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); + _update_zoom_and_panning(true); + accept_event(); + } + + if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE || mb->get_button_index() == MOUSE_BUTTON_RIGHT) { + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_PAN; + } else { + drag_type = DRAG_TYPE_NONE; + } + accept_event(); + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAN) { + panning += mm->get_relative(); + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); accept_event(); } } @@ -103,25 +122,27 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() { return size; } -void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2i p_scroll) { +void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { + float zoom = zoom_widget->get_zoom(); + // Compute the minimum sizes. Size2i base_tiles_control_size = _compute_base_tiles_control_size(); - base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * p_zoom); + base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom); Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size(); - alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * p_zoom); + alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom); // Set the texture for the base tiles. Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); // Set the scales. if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) { - base_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom)); + base_tiles_drawing_root->set_scale(Vector2(zoom, zoom)); } else { base_tiles_drawing_root->set_scale(Vector2(1, 1)); } if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) { - alternative_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom)); + alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom)); } else { alternative_tiles_drawing_root->set_scale(Vector2(1, 1)); } @@ -129,64 +150,40 @@ void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2 // Update the margin container's margins. const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" }; for (int i = 0; i < 4; i++) { - margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * p_zoom); + margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom); } // Update the backgrounds. background_left->update(); background_right->update(); - if (p_scroll != Vector2i(-1, -1)) { - scroll_container->set_h_scroll(p_scroll.x); - scroll_container->set_v_scroll(p_scroll.y); - } - // Zoom on the position. - if (previous_zoom != p_zoom) { - // TODO: solve this. - // There is however an issue with scrollcainter preventing this, as it seems - // that the scrollbars are not updated right aways after its children update. - - // Compute point on previous area. - /*Vector2 max = Vector2(scroll_container->get_h_scrollbar()->get_max(), scroll_container->get_v_scrollbar()->get_max()); - Vector2 min = Vector2(scroll_container->get_h_scrollbar()->get_min(), scroll_container->get_v_scrollbar()->get_min()); - Vector2 value = Vector2(scroll_container->get_h_scrollbar()->get_value(), scroll_container->get_v_scrollbar()->get_value()); - - Vector2 old_max = max * previous_zoom / p_zoom; - - Vector2 max_pixel_change = max - old_max; - Vector2 ratio = ((value + scroll_container->get_local_mouse_position()) / old_max).max(Vector2()).min(Vector2(1,1)); - Vector2 offset = max_pixel_change * ratio; - - print_line("--- ZOOMED ---"); - print_line(vformat("max: %s", max)); - print_line(vformat("min: %s", min)); - print_line(vformat("value: %s", value)); - print_line(vformat("size: %s", scroll_container->get_size())); - print_line(vformat("mouse_pos: %s", scroll_container->get_local_mouse_position())); - - print_line(vformat("ratio: %s", ratio)); - print_line(vformat("max_pixel_change: %s", max_pixel_change)); - print_line(vformat("offset: %s", offset)); - + if (p_zoom_on_mouse_pos) { + // Offset the panning relative to the center of panel. + Vector2 relative_mpos = get_local_mouse_position() - get_size() / 2; + panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos; + } else { + // Center of panel. + panning = panning * zoom / previous_zoom; + } + button_center_view->set_disabled(panning.is_equal_approx(Vector2())); - print_line(vformat("value before: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()))); - scroll_container->set_h_scroll(10000);//scroll_container->get_h_scroll()+offset.x); - scroll_container->set_v_scroll(10000);//scroll_container->get_v_scroll()+offset.y); - print_line(vformat("value after: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()))); - */ + previous_zoom = zoom; - previous_zoom = p_zoom; - } + center_container->set_begin(panning - center_container->get_minimum_size() / 2); + center_container->set_size(center_container->get_minimum_size()); } -void TileAtlasView::_scroll_changed() { - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); +void TileAtlasView::_zoom_widget_changed() { + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); } -void TileAtlasView::_zoom_widget_changed() { - _update_zoom(zoom_widget->get_zoom()); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); +void TileAtlasView::_center_view() { + panning = Vector2(); + button_center_view->set_disabled(true); + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); } void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) { @@ -415,7 +412,7 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ _update_alternative_tiles_rect_cache(); // Update everything. - _update_zoom(zoom_widget->get_zoom()); + _update_zoom_and_panning(); // Change children control size. Size2i base_tiles_control_size = _compute_base_tiles_control_size(); @@ -448,9 +445,10 @@ float TileAtlasView::get_zoom() const { return zoom_widget->get_zoom(); }; -void TileAtlasView::set_transform(float p_zoom, Vector2i p_scroll) { +void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) { zoom_widget->set_zoom(p_zoom); - _update_zoom(zoom_widget->get_zoom(), false, p_scroll); + panning = p_panning; + _update_zoom_and_panning(); }; void TileAtlasView::set_padding(Side p_side, int p_padding) { @@ -525,7 +523,7 @@ Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_a } void TileAtlasView::update() { - scroll_container->update(); + base_tiles_draw->update(); base_tiles_texture_grid->update(); base_tiles_shape_grid->update(); base_tiles_dark->update(); @@ -534,124 +532,149 @@ void TileAtlasView::update() { background_right->update(); } +void TileAtlasView::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: + button_center_view->set_icon(get_theme_icon("CenterView", "EditorIcons")); + break; + } +} + void TileAtlasView::_bind_methods() { + ClassDB::bind_method("_gui_input", &TileAtlasView::_gui_input); + ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll"))); } TileAtlasView::TileAtlasView() { - Panel *panel_container = memnew(Panel); - panel_container->set_h_size_flags(SIZE_EXPAND_FILL); - panel_container->set_v_size_flags(SIZE_EXPAND_FILL); - panel_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - add_child(panel_container); - - //Scrolling - scroll_container = memnew(ScrollContainer); - scroll_container->get_h_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1)); - scroll_container->get_v_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1)); - panel_container->add_child(scroll_container); - scroll_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + + Panel *panel = memnew(Panel); + panel->set_clip_contents(true); + panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + panel->set_h_size_flags(SIZE_EXPAND_FILL); + panel->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(panel); + // Scrollingsc zoom_widget = memnew(EditorZoomWidget); add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1)); - CenterContainer *center_container = memnew(CenterContainer); - center_container->set_h_size_flags(SIZE_EXPAND_FILL); - center_container->set_v_size_flags(SIZE_EXPAND_FILL); + button_center_view = memnew(Button); + button_center_view->set_icon(get_theme_icon("CenterView", "EditorIcons")); + button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5); + button_center_view->connect("pressed", callable_mp(this, &TileAtlasView::_center_view)); + button_center_view->set_flat(true); + button_center_view->set_disabled(true); + add_child(button_center_view); + + center_container = memnew(CenterContainer); + center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + center_container->set_anchors_preset(Control::PRESET_CENTER); center_container->connect("gui_input", callable_mp(this, &TileAtlasView::_gui_input)); - scroll_container->add_child(center_container); + panel->add_child(center_container); missing_source_label = memnew(Label); missing_source_label->set_text(TTR("No atlas source with a valid texture selected.")); center_container->add_child(missing_source_label); margin_container = memnew(MarginContainer); + margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); center_container->add_child(margin_container); hbox = memnew(HBoxContainer); + hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_theme_constant_override("separation", 10); hbox->hide(); margin_container->add_child(hbox); VBoxContainer *left_vbox = memnew(VBoxContainer); + left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_child(left_vbox); VBoxContainer *right_vbox = memnew(VBoxContainer); + right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_child(right_vbox); // Base tiles. Label *base_tile_label = memnew(Label); + base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS); base_tile_label->set_text(TTR("Base Tiles")); base_tile_label->set_align(Label::ALIGN_CENTER); left_vbox->add_child(base_tile_label); base_tiles_root_control = memnew(Control); + base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL); base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input)); left_vbox->add_child(base_tiles_root_control); background_left = memnew(Control); + background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_left->set_anchors_and_offsets_preset(Control::PRESET_WIDE); background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); - background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left)); base_tiles_root_control->add_child(background_left); base_tiles_drawing_root = memnew(Control); + base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); - base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_root_control->add_child(base_tiles_drawing_root); base_tiles_draw = memnew(Control); + base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles)); - base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_draw); base_tiles_texture_grid = memnew(Control); + base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid)); - base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_texture_grid); base_tiles_shape_grid = memnew(Control); + base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid)); - base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_shape_grid); base_tiles_dark = memnew(Control); + base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark)); - base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_dark); // Alternative tiles. Label *alternative_tiles_label = memnew(Label); + alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); alternative_tiles_label->set_text(TTR("Alternative Tiles")); alternative_tiles_label->set_align(Label::ALIGN_CENTER); right_vbox->add_child(alternative_tiles_label); alternative_tiles_root_control = memnew(Control); + alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input)); right_vbox->add_child(alternative_tiles_root_control); background_right = memnew(Control); + background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right)); - background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternative_tiles_root_control->add_child(background_right); alternative_tiles_drawing_root = memnew(Control); - alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_root_control->add_child(alternative_tiles_drawing_root); alternatives_draw = memnew(Control); - alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternative_tiles_drawing_root->add_child(alternatives_draw); } diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 28fd3ed1e0..bafc2b3985 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -34,6 +34,7 @@ #include "editor/editor_zoom_widget.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/center_container.h" #include "scene/gui/label.h" #include "scene/gui/margin_container.h" #include "scene/gui/scroll_container.h" @@ -48,17 +49,24 @@ private: TileSetAtlasSource *tile_set_atlas_source; int source_id = -1; + enum DragType { + DRAG_TYPE_NONE, + DRAG_TYPE_PAN, + }; + DragType drag_type = DRAG_TYPE_NONE; float previous_zoom = 1.0; EditorZoomWidget *zoom_widget; + Button *button_center_view; + CenterContainer *center_container; + Vector2 panning; + void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false); void _zoom_widget_changed(); - void _scroll_changed(); - void _update_zoom(float p_zoom, bool p_zoom_on_mouse_pos = false, Vector2i p_scroll = Vector2i(-1, -1)); + void _center_view(); void _gui_input(const Ref<InputEvent> &p_event); Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); - ScrollContainer *scroll_container; MarginContainer *margin_container; int margin_container_paddings[4] = { 0, 0, 0, 0 }; HBoxContainer *hbox; @@ -102,16 +110,15 @@ private: Size2i _compute_alternative_tiles_control_size(); protected: + void _notification(int p_what); static void _bind_methods(); public: // Global. void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id); - ScrollContainer *get_scroll_container() { return scroll_container; }; - float get_zoom() const; - void set_transform(float p_zoom, Vector2i p_scroll); + void set_transform(float p_zoom, Vector2i p_panning); void set_padding(Side p_side, int p_padding); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 191440bdb3..d9d0e48fb3 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -32,202 +32,2436 @@ #include "tile_set_editor.h" -TileData *TileDataEditor::_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_COND_V(!p_tile_set, nullptr); - ERR_FAIL_COND_V(!p_tile_set->has_source(p_atlas_source_id), nullptr); +#include "core/math/geometry_2d.h" +#include "core/os/keyboard.h" + +#include "editor/editor_properties.h" +#include "editor/editor_scale.h" + +void TileDataEditor::_call_tile_set_changed() { + _tile_set_changed(); +} + +TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) { + ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); + ERR_FAIL_COND_V(!tile_set->has_source(p_cell.source_id), nullptr); TileData *td = nullptr; - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetSource *source = *tile_set->get_source(p_cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { - ERR_FAIL_COND_V(!atlas_source->has_tile(p_atlas_coords), nullptr); - ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_atlas_coords, p_alternative_tile), nullptr); - td = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!atlas_source->has_tile(p_cell.get_atlas_coords()), nullptr); + ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_cell.get_atlas_coords(), p_cell.alternative_tile), nullptr); + td = Object::cast_to<TileData>(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile)); } return td; } -void TileDataEditor::edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { +void TileDataEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("needs_redraw")); } -void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) { + if (tile_set.is_valid()) { + tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + } + tile_set = p_tile_set; + if (tile_set.is_valid()) { + tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + } + _call_tile_set_changed(); +} - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; +bool DummyObject::_set(const StringName &p_name, const Variant &p_value) { + if (properties.has(p_name)) { + properties[p_name] = p_value; + return true; } - ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I); + return false; +} - Vector2i tile_set_tile_size = p_tile_set->get_tile_size(); - Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size); - p_tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), Color(1.0, 0.0, 0.0)); +bool DummyObject::_get(const StringName &p_name, Variant &r_ret) const { + if (properties.has(p_name)) { + r_ret = properties[p_name]; + return true; + } + return false; +} + +bool DummyObject::has_dummy_property(StringName p_name) { + return properties.has(p_name); +} + +void DummyObject::add_dummy_property(StringName p_name) { + ERR_FAIL_COND(properties.has(p_name)); + properties[p_name] = Variant(); +} + +void DummyObject::remove_dummy_property(StringName p_name) { + ERR_FAIL_COND(!properties.has(p_name)); + properties.erase(p_name); +} + +void DummyObject::clear_dummy_properties() { + properties.clear(); +} + +void GenericTilePolygonEditor::_base_control_draw() { + ERR_FAIL_COND(!tile_set.is_valid()); + + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + const Ref<Texture2D> handle = get_theme_icon("EditorPathSharpHandle", "EditorIcons"); + const Ref<Texture2D> add_handle = get_theme_icon("EditorHandleAdd", "EditorIcons"); + + Size2 tile_size = tile_set->get_tile_size(); + + Transform2D xform; + xform.set_origin(base_control->get_size() / 2 + panning); + xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); + base_control->draw_set_transform_matrix(xform); + + // Draw the tile shape filled. + tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), Color(1.0, 1.0, 1.0, 0.3), true); + + // Draw the background. + if (background_texture.is_valid()) { + base_control->draw_texture_rect_region(background_texture, Rect2(-background_region.size / 2 - background_offset, background_region.size), background_region, background_modulate, background_transpose); + } + + // Draw the polygons. + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector<Vector2> &polygon = polygons[i]; + Color color = polygon_color; + if (!in_creation_polygon.is_empty()) { + color = color.darkened(0.3); + } + color.a = 0.5; + Vector<Color> v_color; + v_color.push_back(color); + base_control->draw_polygon(polygon, v_color); + + color.a = 0.7; + for (int j = 0; j < polygon.size(); j++) { + base_control->draw_line(polygon[j], polygon[(j + 1) % polygon.size()], color); + } + } + + // Draw the polygon in creation. + if (!in_creation_polygon.is_empty()) { + for (int i = 0; i < in_creation_polygon.size() - 1; i++) { + base_control->draw_line(in_creation_polygon[i], in_creation_polygon[i + 1], Color(1.0, 1.0, 1.0)); + } + } + + Point2 in_creation_point = xform.affine_inverse().xform(base_control->get_local_mouse_position()); + float in_creation_distance = grab_threshold * 2.0; + _snap_to_tile_shape(in_creation_point, in_creation_distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(in_creation_point); + } + + if (drag_type == DRAG_TYPE_CREATE_POINT && !in_creation_polygon.is_empty()) { + base_control->draw_line(in_creation_polygon[in_creation_polygon.size() - 1], in_creation_point, Color(1.0, 1.0, 1.0)); + } + + // Draw the handles. + int tinted_polygon_index = -1; + int tinted_point_index = -1; + if (drag_type == DRAG_TYPE_DRAG_POINT) { + tinted_polygon_index = drag_polygon_index; + tinted_point_index = drag_point_index; + } else if (hovered_point_index >= 0) { + tinted_polygon_index = hovered_polygon_index; + tinted_point_index = hovered_point_index; + } + + base_control->draw_set_transform_matrix(Transform2D()); + if (!in_creation_polygon.is_empty()) { + for (int i = 0; i < in_creation_polygon.size(); i++) { + base_control->draw_texture(handle, xform.xform(in_creation_polygon[i]) - handle->get_size() / 2); + } + } else { + for (int i = 0; i < (int)polygons.size(); i++) { + const Vector<Vector2> &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + const Color modulate = (tinted_polygon_index == i && tinted_point_index == j) ? Color(0.5, 1, 2) : Color(1, 1, 1); + base_control->draw_texture(handle, xform.xform(polygon[j]) - handle->get_size() / 2, modulate); + } + } + } + + // Draw the text on top of the selected point. + if (tinted_polygon_index >= 0) { + Ref<Font> font = get_theme_font("font", "Label"); + int font_size = get_theme_font_size("font_size", "Label"); + String text = multiple_polygon_mode ? vformat("%d:%d", tinted_polygon_index, tinted_point_index) : vformat("%d", tinted_point_index); + Size2 text_size = font->get_string_size(text, font_size); + base_control->draw_string(font, xform.xform(polygons[tinted_polygon_index][tinted_point_index]) - text_size * 0.5, text, HALIGN_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5)); + } + + if (drag_type == DRAG_TYPE_CREATE_POINT) { + base_control->draw_texture(handle, xform.xform(in_creation_point) - handle->get_size() / 2, Color(0.5, 1, 2)); + } + + // Draw the point creation preview in edit mode. + if (hovered_segment_index >= 0) { + base_control->draw_texture(add_handle, xform.xform(hovered_segment_point) - add_handle->get_size() / 2); + } + + // Draw the tile shape line. + base_control->draw_set_transform_matrix(xform); + tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), grid_color, false); + base_control->draw_set_transform_matrix(Transform2D()); +} + +void GenericTilePolygonEditor::_center_view() { + panning = Vector2(); + base_control->update(); + button_center_view->set_disabled(true); +} + +void GenericTilePolygonEditor::_zoom_changed() { + base_control->update(); +} + +void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { + switch (p_item_pressed) { + case RESET_TO_DEFAULT_TILE: + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "clear_polygons"); + undo_redo->add_do_method(this, "add_polygon", tile_set->get_tile_shape_polygon()); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); + undo_redo->add_undo_method(this, "clear_polygons"); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "add_polygon", polygons[i]); + } + undo_redo->add_undo_method(base_control, "update"); + undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); + undo_redo->commit_action(true); + break; + case CLEAR_TILE: + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "clear_polygons"); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); + undo_redo->add_undo_method(this, "clear_polygons"); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "add_polygon", polygons[i]); + } + undo_redo->add_undo_method(base_control, "update"); + undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); + undo_redo->commit_action(true); + break; + default: + break; + } +} + +void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) { + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + r_polygon_index = -1; + r_point_index = -1; + float closest_distance = grab_threshold + 1.0; + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector<Vector2> &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + float distance = p_pos.distance_to(p_polygon_xform.xform(polygon[j])); + if (distance < grab_threshold && distance < closest_distance) { + r_polygon_index = i; + r_point_index = j; + closest_distance = distance; + } + } + } +} + +void GenericTilePolygonEditor::_grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point) { + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + Point2 point = p_polygon_xform.affine_inverse().xform(p_pos); + r_polygon_index = -1; + r_segment_index = -1; + float closest_distance = grab_threshold * 2.0; + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector<Vector2> &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + Vector2 segment[2] = { polygon[j], polygon[(j + 1) % polygon.size()] }; + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point, segment); + float distance = closest_point.distance_to(point); + if (distance < grab_threshold / editor_zoom_widget->get_zoom() && distance < closest_distance) { + r_polygon_index = i; + r_segment_index = j; + r_point = closest_point; + closest_distance = distance; + } + } + } +} + +void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist) { + ERR_FAIL_COND(!tile_set.is_valid()); + + Vector<Point2> polygon = tile_set->get_tile_shape_polygon(); + Point2 snapped_point = r_point; + + // Snap to polygon vertices. + bool snapped = false; + for (int i = 0; i < polygon.size(); i++) { + float distance = r_point.distance_to(polygon[i]); + if (distance < p_snap_dist && distance < r_current_snapped_dist) { + snapped_point = polygon[i]; + r_current_snapped_dist = distance; + snapped = true; + } + } + + // Snap to edges if we did not snap to vertices. + if (!snapped) { + for (int i = 0; i < polygon.size(); i++) { + Point2 segment[2] = { polygon[i], polygon[(i + 1) % polygon.size()] }; + Point2 point = Geometry2D::get_closest_point_to_segment(r_point, segment); + float distance = r_point.distance_to(point); + if (distance < p_snap_dist && distance < r_current_snapped_dist) { + snapped_point = point; + r_current_snapped_dist = distance; + } + } + } + + r_point = snapped_point; +} + +void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) { + r_point = (r_point * 2).round() / 2.0; +} + +void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) { + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + hovered_polygon_index = -1; + hovered_point_index = -1; + hovered_segment_index = -1; + hovered_segment_point = Vector2(); + + Transform2D xform; + xform.set_origin(base_control->get_size() / 2 + panning); + xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_DRAG_POINT) { + ERR_FAIL_INDEX(drag_polygon_index, (int)polygons.size()); + ERR_FAIL_INDEX(drag_point_index, polygons[drag_polygon_index].size()); + Point2 point = xform.affine_inverse().xform(mm->get_position()); + float distance = grab_threshold * 2.0; + _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(point); + } + polygons[drag_polygon_index].write[drag_point_index] = point; + } else if (drag_type == DRAG_TYPE_PAN) { + panning += mm->get_position() - drag_last_pos; + drag_last_pos = mm->get_position(); + button_center_view->set_disabled(panning.is_equal_approx(Vector2())); + } else { + // Update hovered point. + _grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index); + + // If we have no hovered point, check if we hover a segment. + if (hovered_point_index == -1) { + _grab_polygon_segment_point(mm->get_position(), xform, hovered_polygon_index, hovered_segment_index, hovered_segment_point); + } + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_ctrl_pressed()) { + editor_zoom_widget->set_zoom_by_increments(1); + _zoom_changed(); + accept_event(); + } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_ctrl_pressed()) { + editor_zoom_widget->set_zoom_by_increments(-1); + _zoom_changed(); + accept_event(); + } else if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (tools_button_group->get_pressed_button() != button_create) { + in_creation_polygon.clear(); + } + if (tools_button_group->get_pressed_button() == button_create) { + // Create points. + if (in_creation_polygon.size() >= 3 && mb->get_position().distance_to(xform.xform(in_creation_polygon[0])) < grab_threshold) { + // Closes and create polygon. + if (!multiple_polygon_mode) { + clear_polygons(); + } + int added = add_polygon(in_creation_polygon); + + in_creation_polygon.clear(); + button_edit->set_pressed(true); + undo_redo->create_action(TTR("Edit Polygons")); + if (!multiple_polygon_mode) { + undo_redo->add_do_method(this, "clear_polygons"); + } + undo_redo->add_do_method(this, "add_polygon", in_creation_polygon); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(this, "remove_polygon", added); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else { + // Create a new point. + drag_type = DRAG_TYPE_CREATE_POINT; + } + } else if (tools_button_group->get_pressed_button() == button_edit) { + // Edit points. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + drag_type = DRAG_TYPE_DRAG_POINT; + drag_polygon_index = closest_polygon; + drag_point_index = closest_point; + drag_old_polygon = polygons[drag_polygon_index]; + } else { + // Create a point. + Vector2 point_to_create; + _grab_polygon_segment_point(mb->get_position(), xform, closest_polygon, closest_point, point_to_create); + if (closest_polygon >= 0) { + polygons[closest_polygon].insert(closest_point + 1, point_to_create); + drag_type = DRAG_TYPE_DRAG_POINT; + drag_polygon_index = closest_polygon; + drag_point_index = closest_point + 1; + drag_old_polygon = polygons[closest_polygon]; + } + } + } else if (tools_button_group->get_pressed_button() == button_delete) { + // Remove point. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + PackedVector2Array old_polygon = polygons[closest_polygon]; + polygons[closest_polygon].remove(closest_point); + undo_redo->create_action(TTR("Edit Polygons")); + if (polygons[closest_polygon].size() < 3) { + remove_polygon(closest_polygon); + undo_redo->add_do_method(this, "remove_polygon", closest_polygon); + undo_redo->add_undo_method(this, "add_polygon", old_polygon, closest_polygon); + } else { + undo_redo->add_do_method(this, "set_polygon", closest_polygon, polygons[closest_polygon]); + undo_redo->add_undo_method(this, "set_polygon", closest_polygon, old_polygon); + } + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } + } + } else { + if (drag_type == DRAG_TYPE_DRAG_POINT) { + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "set_polygon", drag_polygon_index, polygons[drag_polygon_index]); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(this, "set_polygon", drag_polygon_index, drag_old_polygon); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else if (drag_type == DRAG_TYPE_CREATE_POINT) { + Point2 point = xform.affine_inverse().xform(mb->get_position()); + float distance = grab_threshold * 2; + _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(point); + } + in_creation_polygon.push_back(point); + } + drag_type = DRAG_TYPE_NONE; + drag_point_index = -1; + } + + } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { + if (mb->is_pressed()) { + if (tools_button_group->get_pressed_button() == button_edit) { + // Remove point or pan. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + PackedVector2Array old_polygon = polygons[closest_polygon]; + polygons[closest_polygon].remove(closest_point); + undo_redo->create_action(TTR("Edit Polygons")); + if (polygons[closest_polygon].size() < 3) { + remove_polygon(closest_polygon); + undo_redo->add_do_method(this, "remove_polygon", closest_polygon); + undo_redo->add_undo_method(this, "add_polygon", old_polygon, closest_polygon); + } else { + undo_redo->add_do_method(this, "set_polygon", closest_polygon, polygons[closest_polygon]); + undo_redo->add_undo_method(this, "set_polygon", closest_polygon, old_polygon); + } + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } + } else { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } + } else { + drag_type = DRAG_TYPE_NONE; + } + } else if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE) { + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } else { + drag_type = DRAG_TYPE_NONE; + } + } + } + + base_control->update(); +} + +void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) { + if (tile_set != p_tile_set) { + // Set the default tile shape + clear_polygons(); + if (p_tile_set.is_valid()) { + add_polygon(p_tile_set->get_tile_shape_polygon()); + } + } + tile_set = p_tile_set; +} + +void GenericTilePolygonEditor::set_background(Ref<Texture2D> p_texture, Rect2 p_region, Vector2 p_offset, bool p_flip_h, bool p_flip_v, bool p_transpose, Color p_modulate) { + background_texture = p_texture; + background_region = p_region; + background_offset = p_offset; + background_h_flip = p_flip_h; + background_v_flip = p_flip_v; + background_transpose = p_transpose; + background_modulate = p_modulate; + base_control->update(); +} + +int GenericTilePolygonEditor::get_polygon_count() { + return polygons.size(); +} + +int GenericTilePolygonEditor::add_polygon(Vector<Point2> p_polygon, int p_index) { + ERR_FAIL_COND_V(p_polygon.size() < 3, -1); + ERR_FAIL_COND_V(!multiple_polygon_mode && polygons.size() >= 1, -1); + + if (p_index < 0) { + polygons.push_back(p_polygon); + base_control->update(); + button_edit->set_pressed(true); + return polygons.size() - 1; + } else { + polygons.insert(p_index, p_polygon); + button_edit->set_pressed(true); + base_control->update(); + return p_index; + } +} + +void GenericTilePolygonEditor::remove_polygon(int p_index) { + ERR_FAIL_INDEX(p_index, (int)polygons.size()); + polygons.remove(p_index); + + if (polygons.size() == 0) { + button_create->set_pressed(true); + } + base_control->update(); +} + +void GenericTilePolygonEditor::clear_polygons() { + polygons.clear(); + base_control->update(); +} + +void GenericTilePolygonEditor::set_polygon(int p_polygon_index, Vector<Point2> p_polygon) { + ERR_FAIL_INDEX(p_polygon_index, (int)polygons.size()); + ERR_FAIL_COND(p_polygon.size() < 3); + polygons[p_polygon_index] = p_polygon; + button_edit->set_pressed(true); + base_control->update(); } -void TileDataIntegerEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +Vector<Point2> GenericTilePolygonEditor::get_polygon(int p_polygon_index) { + ERR_FAIL_INDEX_V(p_polygon_index, (int)polygons.size(), Vector<Point2>()); + return polygons[p_polygon_index]; +} + +void GenericTilePolygonEditor::set_polygons_color(Color p_color) { + polygon_color = p_color; + base_control->update(); +} + +void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon_mode) { + multiple_polygon_mode = p_multiple_polygon_mode; +} + +void GenericTilePolygonEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: + button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCreate", "EditorIcons")); + button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveEdit", "EditorIcons")); + button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveDelete", "EditorIcons")); + button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CenterView", "EditorIcons")); + button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Snap", "EditorIcons")); + button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("GuiTabMenu", "EditorIcons")); + break; + } +} + +void GenericTilePolygonEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_polygon_count"), &GenericTilePolygonEditor::get_polygon_count); + ClassDB::bind_method(D_METHOD("add_polygon", "polygon", "index"), &GenericTilePolygonEditor::add_polygon, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_polygon", "index"), &GenericTilePolygonEditor::remove_polygon); + ClassDB::bind_method(D_METHOD("clear_polygons"), &GenericTilePolygonEditor::clear_polygons); + ClassDB::bind_method(D_METHOD("set_polygon", "index", "polygon"), &GenericTilePolygonEditor::set_polygon); + ClassDB::bind_method(D_METHOD("get_polygon", "index"), &GenericTilePolygonEditor::set_polygon); + + ADD_SIGNAL(MethodInfo("polygons_changed")); +} + +GenericTilePolygonEditor::GenericTilePolygonEditor() { + toolbar = memnew(HBoxContainer); + add_child(toolbar); + + tools_button_group.instantiate(); + + button_create = memnew(Button); + button_create->set_flat(true); + button_create->set_toggle_mode(true); + button_create->set_button_group(tools_button_group); + button_create->set_pressed(true); + toolbar->add_child(button_create); + + button_edit = memnew(Button); + button_edit->set_flat(true); + button_edit->set_toggle_mode(true); + button_edit->set_button_group(tools_button_group); + toolbar->add_child(button_edit); + + button_delete = memnew(Button); + button_delete->set_flat(true); + button_delete->set_toggle_mode(true); + button_delete->set_button_group(tools_button_group); + toolbar->add_child(button_delete); + + button_advanced_menu = memnew(MenuButton); + button_advanced_menu->set_flat(true); + button_advanced_menu->set_toggle_mode(true); + button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE); + button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE); + button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed)); + toolbar->add_child(button_advanced_menu); + + toolbar->add_child(memnew(VSeparator)); + + button_pixel_snap = memnew(Button); + button_pixel_snap->set_flat(true); + button_pixel_snap->set_toggle_mode(true); + button_pixel_snap->set_pressed(true); + toolbar->add_child(button_pixel_snap); + + Control *root = memnew(Control); + root->set_h_size_flags(Control::SIZE_EXPAND_FILL); + root->set_custom_minimum_size(Size2(0, 200 * EDSCALE)); + root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + add_child(root); + + panel = memnew(Panel); + panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + root->add_child(panel); + + base_control = memnew(Control); + base_control->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + base_control->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + base_control->connect("draw", callable_mp(this, &GenericTilePolygonEditor::_base_control_draw)); + base_control->connect("gui_input", callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input)); + base_control->set_clip_contents(true); + root->add_child(base_control); + + editor_zoom_widget = memnew(EditorZoomWidget); + editor_zoom_widget->set_position(Vector2(5, 5)); + editor_zoom_widget->connect("zoom_changed", callable_mp(this, &GenericTilePolygonEditor::_zoom_changed).unbind(1)); + root->add_child(editor_zoom_widget); + + button_center_view = memnew(Button); + button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CenterView", "EditorIcons")); + button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5); + button_center_view->connect("pressed", callable_mp(this, &GenericTilePolygonEditor::_center_view)); + button_center_view->set_flat(true); + button_center_view->set_disabled(true); + root->add_child(button_center_view); +} + +void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + ERR_FAIL_COND(!dummy_object); + dummy_object->set(p_property, p_value); +} + +Variant TileDataDefaultEditor::_get_painted_value() { + ERR_FAIL_COND_V(!dummy_object, Variant()); + return dummy_object->get(property); +} + +void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); ERR_FAIL_COND(!tile_data); + Variant value = tile_data->get(property); + dummy_object->set(property, value); + if (property_editor) { + property_editor->update_property(); + } +} - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; +void TileDataDefaultEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + tile_data->set(property, p_value); +} + +Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get(property); +} + +void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { + for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), p_new_value); } - ERR_FAIL_COND(value.get_type() != Variant::INT); +} - Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); - int height = font->get_height(); - int width = 200; - p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%d", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1)); +void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + if (drag_type == DRAG_TYPE_PAINT_RECT) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + + p_canvas_item->draw_set_transform_matrix(p_transform); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); + + Set<TileMapCell> edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + + for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + } +}; + +void TileDataDefaultEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){ + +}; + +void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT) { + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + if (!drag_modified.has(cell)) { + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + } + _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + _set_painted_value(p_tile_set_atlas_source, coords, 0); + picker_button->set_pressed(false); + } + } else if (mb->is_ctrl_pressed()) { + drag_type = DRAG_TYPE_PAINT_RECT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + drag_start_pos = mb->get_position(); + } else { + drag_type = DRAG_TYPE_PAINT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); + } + drag_last_pos = mb->get_position(); + } + } else { + if (drag_type == DRAG_TYPE_PAINT_RECT) { + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + drag_modified.clear(); + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + } + } + } + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT) { + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } + } + } + } } -void TileDataFloatEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + if (!drag_modified.has(cell)) { + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); + } + _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); + } + + drag_last_pos = mm->get_position(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + _set_painted_value(p_tile_set_atlas_source, coords, alternative_tile); + picker_button->set_pressed(false); + } + } else { + drag_type = DRAG_TYPE_PAINT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); + _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); + } + drag_last_pos = mb->get_position(); + } + } else { + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } + } + } +} + +void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); bool valid; - Variant value = tile_data->get(p_property, &valid); + Variant value = tile_data->get(property, &valid); if (!valid) { return; } - ERR_FAIL_COND(value.get_type() != Variant::FLOAT); - Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); - int height = font->get_height(); - int width = 200; - p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%.2f", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1)); + if (value.get_type() == Variant::BOOL) { + Ref<Texture2D> texture = (bool)value ? tile_bool_checked : tile_bool_unchecked; + int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + p_canvas_item->draw_texture_rect(texture, rect); + } else if (value.get_type() == Variant::COLOR) { + int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + p_canvas_item->draw_rect(rect, value); + } else { + Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + String text; + switch (value.get_type()) { + case Variant::INT: + text = vformat("%d", value); + break; + case Variant::FLOAT: + text = vformat("%.2f", value); + break; + case Variant::STRING: + case Variant::STRING_NAME: + text = value; + break; + default: + return; + break; + } + + Color color = Color(1, 1, 1); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.set_v(0.9); + color = selection_color; + } else if (is_visible_in_tree()) { + Variant painted_value = _get_painted_value(); + bool equal = (painted_value.get_type() == Variant::FLOAT && value.get_type() == Variant::FLOAT) ? Math::is_equal_approx(float(painted_value), float(value)) : painted_value == value; + if (equal) { + color = Color(0.7, 0.7, 0.7); + } + } + + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } } -void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p_property, String p_label, Variant p_default_value) { + ERR_FAIL_COND_MSG(!property.is_empty(), "Cannot setup TileDataDefaultEditor twice"); + property = p_property; + + // Update everything. + if (property_editor) { + property_editor->queue_delete(); + } + + // Update the dummy object. + dummy_object->add_dummy_property(p_property); + + // Get the default value for the type. + if (p_default_value == Variant()) { + Callable::CallError error; + Variant painted_value; + Variant::construct(p_type, painted_value, nullptr, 0, error); + dummy_object->set(p_property, painted_value); + } else { + dummy_object->set(p_property, p_default_value); + } + + // Create and setup the property editor. + property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + property_editor->set_object_and_property(dummy_object, p_property); + if (p_label.is_empty()) { + property_editor->set_label(p_property); + } else { + property_editor->set_label(p_label); + } + property_editor->connect("property_changed", callable_mp(this, &TileDataDefaultEditor::_property_value_changed).unbind(1)); + property_editor->update_property(); + add_child(property_editor); +} + +void TileDataDefaultEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + tile_bool_checked = get_theme_icon("TileChecked", "EditorIcons"); + tile_bool_unchecked = get_theme_icon("TileUnchecked", "EditorIcons"); + break; + default: + break; + } +} + +TileDataDefaultEditor::TileDataDefaultEditor() { + label = memnew(Label); + label->set_text("Painting:"); + add_child(label); + + toolbar->add_child(memnew(VSeparator)); + + picker_button = memnew(Button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P)); + toolbar->add_child(picker_button); +} + +TileDataDefaultEditor::~TileDataDefaultEditor() { + toolbar->queue_delete(); + memdelete(dummy_object); +} + +void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + Vector2i tile_set_tile_size = tile_set->get_tile_size(); + Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size); + Color color = Color(1.0, 0.0, 0.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; + } + tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), color); +} + +void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); bool valid; - Variant value = tile_data->get(p_property, &valid); + Variant value = tile_data->get(property, &valid); if (!valid) { return; } ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2); + Color color = Color(1.0, 1.0, 1.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; + } Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); - p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2, color); } -void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; + Color color = Color(1.0, 1.0, 1.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; } - ERR_FAIL_COND(value.get_type() != Variant::INT); - Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); - p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, value)) - position_icon->get_size() / 2); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, tile_data->get_y_sort_origin())) - position_icon->get_size() / 2, color); } -void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); - Vector<String> components = String(p_property).split("/", true); - if (components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - int occlusion_layer = components[0].trim_prefix("occlusion_layer_").to_int(); - if (occlusion_layer >= 0 && occlusion_layer < p_tile_set->get_occlusion_layers_count()) { - // Draw all shapes. - Vector<Color> debug_occlusion_color; - debug_occlusion_color.push_back(Color(0.5, 0, 0, 0.6)); + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + Color color = grid_color.darkened(0.2); + if (p_selected) { + color = selection_color.darkened(0.2); + } + color.a *= 0.5; - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - Ref<OccluderPolygon2D> occluder = tile_data->get_occluder(occlusion_layer); - if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { - p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); - } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); + Vector<Color> debug_occlusion_color; + debug_occlusion_color.push_back(color); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + Ref<OccluderPolygon2D> occluder = tile_data->get_occluder(occlusion_layer); + if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { + p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Variant TileDataOcclusionShapeEditor::_get_painted_value() { + Ref<OccluderPolygon2D> occluder_polygon; + occluder_polygon.instantiate(); + if (polygon_editor->get_polygon_count() >= 1) { + occluder_polygon->set_polygon(polygon_editor->get_polygon(0)); + } + return occluder_polygon; +} + +void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + + Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer); + polygon_editor->clear_polygons(); + if (occluder_polygon.is_valid()) { + polygon_editor->add_polygon(occluder_polygon->get_polygon()); + } + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + Ref<OccluderPolygon2D> occluder_polygon = p_value; + tile_data->set_occluder(occlusion_layer, occluder_polygon); + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get_occluder(occlusion_layer); +} + +void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { + for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), p_new_value); + } +} + +void TileDataOcclusionShapeEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); +} + +void TileDataOcclusionShapeEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); + break; + default: + break; + } +} + +TileDataOcclusionShapeEditor::TileDataOcclusionShapeEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + add_child(polygon_editor); +} + +void TileDataCollisionEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + dummy_object->set(p_property, p_value); +} + +void TileDataCollisionEditor::_polygons_changed() { + // Update the dummy object properties and their editors. + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + StringName one_way_property = vformat("polygon_%d_one_way", i); + StringName one_way_margin_property = vformat("polygon_%d_one_way_margin", i); + + if (!dummy_object->has_dummy_property(one_way_property)) { + dummy_object->add_dummy_property(one_way_property); + dummy_object->set(one_way_property, false); } + + if (!dummy_object->has_dummy_property(one_way_margin_property)) { + dummy_object->add_dummy_property(one_way_margin_property); + dummy_object->set(one_way_margin_property, 1.0); + } + + if (!property_editors.has(one_way_property)) { + EditorProperty *one_way_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::BOOL, one_way_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + one_way_property_editor->set_object_and_property(dummy_object, one_way_property); + one_way_property_editor->set_label(one_way_property); + one_way_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); + one_way_property_editor->update_property(); + add_child(one_way_property_editor); + property_editors[one_way_property] = one_way_property_editor; + } + + if (!property_editors.has(one_way_margin_property)) { + EditorProperty *one_way_margin_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, one_way_margin_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + one_way_margin_property_editor->set_object_and_property(dummy_object, one_way_margin_property); + one_way_margin_property_editor->set_label(one_way_margin_property); + one_way_margin_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); + one_way_margin_property_editor->update_property(); + add_child(one_way_margin_property_editor); + property_editors[one_way_margin_property] = one_way_margin_property_editor; + } + } + + // Remove uneeded properties and their editors. + for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way", i)); i++) { + dummy_object->remove_dummy_property(vformat("polygon_%d_one_way", i)); + } + for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way_margin", i)); i++) { + dummy_object->remove_dummy_property(vformat("polygon_%d_one_way_margin", i)); + } + for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way", i)); i++) { + property_editors[vformat("polygon_%d_one_way", i)]->queue_delete(); + property_editors.erase(vformat("polygon_%d_one_way", i)); } + for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way_margin", i)); i++) { + property_editors[vformat("polygon_%d_one_way_margin", i)]->queue_delete(); + property_editors.erase(vformat("polygon_%d_one_way_margin", i)); + } +} + +Variant TileDataCollisionEditor::_get_painted_value() { + Array array; + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant()); + Dictionary dict; + dict["points"] = polygon_editor->get_polygon(i); + dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i)); + dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i)); + array.push_back(dict); + } + + return array; } -void TileDataCollisionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); ERR_FAIL_COND(!tile_data); - Vector<String> components = String(p_property).split("/", true); - if (components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - int physics_layer = components[0].trim_prefix("physics_layer_").to_int(); - if (physics_layer >= 0 && physics_layer < p_tile_set->get_physics_layers_count()) { - // Draw all shapes. - Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color(); - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - for (int i = 0; i < tile_data->get_collision_shapes_count(physics_layer); i++) { - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(physics_layer, i); - if (shape.is_valid()) { - shape->draw(p_canvas_item->get_canvas_item(), debug_collision_color); - } - } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); + polygon_editor->clear_polygons(); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(physics_layer, i); + if (polygon.size() >= 3) { + polygon_editor->add_polygon(polygon); } } + + _polygons_changed(); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + dummy_object->set(vformat("polygon_%d_one_way", i), tile_data->is_collision_polygon_one_way(physics_layer, i)); + dummy_object->set(vformat("polygon_%d_one_way_margin", i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i)); + } + for (Map<StringName, EditorProperty *>::Element *E = property_editors.front(); E; E = E->next()) { + E->get()->update_property(); + } + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } -void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); ERR_FAIL_COND(!tile_data); - Vector<String> components = String(p_property).split("/", true); - if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") { - TileSetPluginAtlasTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data); + Array array = p_value; + tile_data->set_collision_polygons_count(physics_layer, array.size()); + for (int i = 0; i < array.size(); i++) { + Dictionary dict = array[i]; + tile_data->set_collision_polygon_points(physics_layer, i, dict["points"]); + tile_data->set_collision_polygon_one_way(physics_layer, i, dict["one_way"]); + tile_data->set_collision_polygon_one_way_margin(physics_layer, i, dict["one_way_margin"]); + } + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + + Array array; + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Dictionary dict; + dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i); + dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i); + dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i); + array.push_back(dict); + } + return array; +} + +void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { + Array new_array = p_new_value; + for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) { + Array old_array = E->get(); + + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), old_array.size()); + for (int i = 0; i < old_array.size(); i++) { + Dictionary dict = old_array[i]; + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]); + } + + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), new_array.size()); + for (int i = 0; i < new_array.size(); i++) { + Dictionary dict = new_array[i]; + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]); + } + } +} + +void TileDataCollisionEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); + _polygons_changed(); +} + +void TileDataCollisionEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); + break; + default: + break; } } -void TileDataNavigationPolygonEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +TileDataCollisionEditor::TileDataCollisionEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + polygon_editor->set_multiple_polygon_mode(true); + polygon_editor->connect("polygons_changed", callable_mp(this, &TileDataCollisionEditor::_polygons_changed)); + add_child(polygon_editor); + + _polygons_changed(); +} + +TileDataCollisionEditor::~TileDataCollisionEditor() { + memdelete(dummy_object); +} + +void TileDataCollisionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); - Vector<String> components = String(p_property).split("/", true); - if (components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - int navigation_layer = components[0].trim_prefix("navigation_layer_").to_int(); - if (navigation_layer >= 0 && navigation_layer < p_tile_set->get_navigation_layers_count()) { - // Draw all shapes. - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + // Draw all shapes. + Vector<Color> color; + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.a = 0.7; + color.push_back(selection_color); + } else { + Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color(); + color.push_back(debug_collision_color); + } + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(physics_layer, i); + if (polygon.size() >= 3) { + p_canvas_item->draw_polygon(polygon, color); + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +void TileDataTerrainsEditor::_update_terrain_selector() { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Update the terrain set selector. + Vector<String> options; + options.push_back(String(TTR("No terrains")) + String(":-1")); + for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) { + options.push_back(vformat("Terrain Set %d", i)); + } + terrain_set_property_editor->setup(options); + terrain_set_property_editor->update_property(); + + // Update the terrain selector. + int terrain_set = int(dummy_object->get("terrain_set")); + if (terrain_set == -1) { + terrain_property_editor->hide(); + } else { + options.clear(); + Vector<Vector<Ref<Texture2D>>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE); + options.push_back(String(TTR("No terrain")) + String(":-1")); + for (int i = 0; i < tile_set->get_terrains_count(terrain_set); i++) { + String name = tile_set->get_terrain_name(terrain_set, i); + if (name.is_empty()) { + options.push_back(vformat("Terrain %d", i)); + } else { + options.push_back(name); + } + } + terrain_property_editor->setup(options); + terrain_property_editor->update_property(); + + // Kind of a hack to set icons. + // We could provide a way to modify that in the EditorProperty. + OptionButton *option_button = Object::cast_to<OptionButton>(terrain_property_editor->get_child(0)); + for (int terrain = 0; terrain < tile_set->get_terrains_count(terrain_set); terrain++) { + option_button->set_item_icon(terrain + 1, icons[terrain_set][terrain]); + } + terrain_property_editor->show(); + } +} + +void TileDataTerrainsEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + Variant old_value = dummy_object->get(p_property); + dummy_object->set(p_property, p_value); + if (p_property == "terrain_set") { + if (p_value != old_value) { + dummy_object->set("terrain", -1); + } + _update_terrain_selector(); + } + emit_signal("needs_redraw"); +} + +void TileDataTerrainsEditor::_tile_set_changed() { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Fix if wrong values are selected. + if (int(dummy_object->get("terrain_set")) > tile_set->get_terrain_sets_count()) { + dummy_object->set("terrain_set", -1); + } + int terrain_set = int(dummy_object->get("terrain")); + if (terrain_set >= 0) { + if (int(dummy_object->get("terrain")) > tile_set->get_terrains_count(terrain_set)) { + dummy_object->set("terrain", -1); + } + } + + _update_terrain_selector(); +} + +void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + ERR_FAIL_COND(!tile_set.is_valid()); - Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); - if (navigation_polygon.is_valid()) { - Vector<Vector2> verts = navigation_polygon->get_vertices(); - if (verts.size() < 3) { - return; + // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. + Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; + if (drag_type == DRAG_TYPE_NONE) { + Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + hovered_coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_pos); + hovered_coords = p_tile_set_atlas_source->get_tile_at_coords(hovered_coords); + if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, 0)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0); + + if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set"))) { + // Draw hovered bit. + Transform2D xform; + xform.set_origin(position); + + Vector<Color> color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } + } } + } else { + // Draw hovered tile. + Vector2i tile_size = tile_set->get_tile_size(); + Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size)); + tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true); + } + } + } + + // Dim terrains with wrong terrain set. + Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); + if (coords != hovered_coords) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) { + // Dimming + p_canvas_item->draw_set_transform_matrix(p_transform); + Rect2i rect = p_tile_set_atlas_source->get_tile_texture_region(coords); + p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); + + // Text + p_canvas_item->draw_set_transform_matrix(Transform2D()); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Color color = Color(1, 1, 1); + String text; + if (tile_data->get_terrain_set() >= 0) { + text = vformat("%d", tile_data->get_terrain_set()); + } else { + text = "-"; + } + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } + } + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { + // Draw selection rectangle. + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + + p_canvas_item->draw_set_transform_matrix(p_transform); - Color color = p_canvas_item->get_tree()->get_debug_navigation_color(); + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); - RandomPCG rand; - for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector<int> polygon = navigation_polygon->get_polygon(i); - Vector<Vector2> vertices; - vertices.resize(polygon.size()); + Set<TileMapCell> edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + + for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { + // Highlight selected peering bits. + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); + + Set<TileMapCell> edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + } + + Vector2 end = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + Vector<Point2> mouse_pos_rect_polygon; + mouse_pos_rect_polygon.push_back(drag_start_pos); + mouse_pos_rect_polygon.push_back(Vector2(end.x, drag_start_pos.y)); + mouse_pos_rect_polygon.push_back(end); + mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, end.y)); + + Vector<Color> color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + p_canvas_item->draw_set_transform_matrix(p_transform); + + for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], verts.size()); - vertices.write[j] = verts[polygon[j]]; + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw bit. + p_canvas_item->draw_polygon(polygon, color); + } + } + } + } + + p_canvas_item->draw_set_transform_matrix(Transform2D()); + } +} + +void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. + Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; + int hovered_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; + if (drag_type == DRAG_TYPE_NONE) { + Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + Vector3i hovered = p_tile_atlas_view->get_alternative_tile_at_pos(mouse_pos); + hovered_coords = Vector2i(hovered.x, hovered.y); + hovered_alternative = hovered.z; + if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative); + + if (terrain_set == int(dummy_object->get("terrain_set"))) { + // Draw hovered bit. + Transform2D xform; + xform.set_origin(position); + + Vector<Color> color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } + } + } + } else { + // Draw hovered tile. + Vector2i tile_size = tile_set->get_tile_size(); + Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size)); + tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true); + } + } + } + + // Dim terrains with wrong terrain set. + Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); + for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) { + int alternative_tile = p_tile_set_atlas_source->get_alternative_tile_id(coords, j); + if (coords != hovered_coords || alternative_tile != hovered_alternative) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) { + // Dimming + p_canvas_item->draw_set_transform_matrix(p_transform); + Rect2i rect = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); + + // Text + p_canvas_item->draw_set_transform_matrix(Transform2D()); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Color color = Color(1, 1, 1); + String text; + if (tile_data->get_terrain_set() >= 0) { + text = vformat("%d", tile_data->get_terrain_set()); + } else { + text = "-"; + } + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } + } + } + } + + p_canvas_item->draw_set_transform_matrix(Transform2D()); +} + +void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + int terrain_set = drag_painted_value; + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + // Save the old terrain_set and terrains bits. + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + + // Set the terrain_set. + tile_data->set_terrain_set(terrain_set); + } + } + drag_last_pos = mm->get_position(); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + int terrain_set = Dictionary(drag_painted_value)["terrain_set"]; + int terrain = Dictionary(drag_painted_value)["terrain"]; + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + // Save the old terrain_set and terrains bits. + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + + // Set the terrains bits. + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + dummy_object->set("terrain_set", terrain_set); + dummy_object->set("terrain", -1); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + } + } + } + terrain_set_property_editor->update_property(); + _update_terrain_selector(); + picker_button->set_pressed(false); + } + } else { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + TileData *tile_data = nullptr; + if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + } + int terrain_set = int(dummy_object->get("terrain_set")); + int terrain = int(dummy_object->get("terrain")); + if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { + if (mb->is_ctrl_pressed()) { + // Paint terrain set with rect. + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT; + drag_modified.clear(); + drag_painted_value = terrain_set; + drag_start_pos = mb->get_position(); + } else { + // Paint terrain set. + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; + drag_modified.clear(); + drag_painted_value = terrain_set; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain_set. + tile_data->set_terrain_set(terrain_set); + } + drag_last_pos = mb->get_position(); + } + } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + if (mb->is_ctrl_pressed()) { + // Paint terrain set with rect. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + drag_start_pos = mb->get_position(); + } else { + // Paint terrain bits. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain bit. + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + drag_last_pos = mb->get_position(); + } + } + } + } else { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + Set<TileMapCell> edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + undo_redo->create_action(TTR("Painting Terrain Set")); + for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), tile_data->get_terrain_set()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), drag_painted_value); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), tile_data->get_peering_bit_terrain(bit)); + } + } + } + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + undo_redo->create_action(TTR("Painting Terrain Set")); + for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + undo_redo->create_action(TTR("Painting Terrain")); + for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain); + } + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + Set<TileMapCell> edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + } + + Vector<Point2> mouse_pos_rect_polygon; + mouse_pos_rect_polygon.push_back(drag_start_pos); + mouse_pos_rect_polygon.push_back(Vector2(mb->get_position().x, drag_start_pos.y)); + mouse_pos_rect_polygon.push_back(mb->get_position()); + mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, mb->get_position().y)); + + undo_redo->create_action(TTR("Painting Terrain")); + for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + for (int j = 0; j < polygon.size(); j++) { + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw bit. + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), terrain); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), tile_data->get_peering_bit_terrain(bit)); + } + } + } + } + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } + } + } + } +} + +void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + tile_data->set_terrain_set(drag_painted_value); + } + + drag_last_pos = mm->get_position(); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + + // Save the old terrain_set and terrains bits. + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (tile_data->get_terrain_set() == terrain_set) { + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + + // Set the terrains bits. + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + dummy_object->set("terrain_set", terrain_set); + dummy_object->set("terrain", -1); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + } + } + } + terrain_set_property_editor->update_property(); + _update_terrain_selector(); + picker_button->set_pressed(false); } + } else { + int terrain_set = int(dummy_object->get("terrain_set")); + int terrain = int(dummy_object->get("terrain")); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); - random_variation_color.a = color.a; - Vector<Color> colors; - colors.push_back(random_variation_color); + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); - RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors); + if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; + drag_modified.clear(); + drag_painted_value = int(dummy_object->get("terrain_set")); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + tile_data->set_terrain_set(drag_painted_value); + } + drag_last_pos = mb->get_position(); + } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + // Paint terrain bits. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain bit. + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + drag_last_pos = mb->get_position(); + } + } + } else { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + undo_redo->create_action(TTR("Painting Tiles Property")); + for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + undo_redo->create_action(TTR("Painting Terrain")); + for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain); + } + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; } } + } + } +} + +void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); + tile_set->draw_terrains(p_canvas_item, p_transform, tile_data); +} + +void TileDataTerrainsEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + break; + default: + break; + } +} + +TileDataTerrainsEditor::TileDataTerrainsEditor() { + label = memnew(Label); + label->set_text("Painting:"); + add_child(label); + + // Toolbar + toolbar->add_child(memnew(VSeparator)); + + picker_button = memnew(Button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P)); + toolbar->add_child(picker_button); + + // Setup + dummy_object->add_dummy_property("terrain_set"); + dummy_object->set("terrain_set", -1); + dummy_object->add_dummy_property("terrain"); + dummy_object->set("terrain", -1); + + // Get the default value for the type. + terrain_set_property_editor = memnew(EditorPropertyEnum); + terrain_set_property_editor->set_object_and_property(dummy_object, "terrain_set"); + terrain_set_property_editor->set_label("Terrain Set"); + terrain_set_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); + add_child(terrain_set_property_editor); + + terrain_property_editor = memnew(EditorPropertyEnum); + terrain_property_editor->set_object_and_property(dummy_object, "terrain"); + terrain_property_editor->set_label("Terrain"); + terrain_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); + add_child(terrain_property_editor); +} + +TileDataTerrainsEditor::~TileDataTerrainsEditor() { + toolbar->queue_delete(); + memdelete(dummy_object); +} + +Variant TileDataNavigationEditor::_get_painted_value() { + Ref<NavigationPolygon> navigation_polygon; + navigation_polygon.instantiate(); + + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + Vector<Vector2> polygon = polygon_editor->get_polygon(i); + navigation_polygon->add_outline(polygon); + } + + navigation_polygon->make_polygons_from_outlines(); + return navigation_polygon; +} + +void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); + polygon_editor->clear_polygons(); + if (navigation_polygon.is_valid()) { + for (int i = 0; i < navigation_polygon->get_outline_count(); i++) { + polygon_editor->add_polygon(navigation_polygon->get_outline(i)); + } + } + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + Ref<NavigationPolygon> navigation_polygon = p_value; + tile_data->set_navigation_polygon(navigation_layer, navigation_polygon); + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get_navigation_polygon(navigation_layer); +} + +void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) { + for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), p_new_value); + } +} + +void TileDataNavigationEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); +} + +void TileDataNavigationEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_navigation_color()); + break; + default: + break; + } +} + +TileDataNavigationEditor::TileDataNavigationEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + polygon_editor->set_multiple_polygon_mode(true); + add_child(polygon_editor); +} + +void TileDataNavigationEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + // Draw all shapes. + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); + if (navigation_polygon.is_valid()) { + Vector<Vector2> verts = navigation_polygon->get_vertices(); + if (verts.size() < 3) { + return; + } + + Color color = p_canvas_item->get_tree()->get_debug_navigation_color(); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.a = 0.7; + color = selection_color; + } + + RandomPCG rand; + for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector<int> polygon = navigation_polygon->get_polygon(i); + Vector<Vector2> vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], verts.size()); + vertices.write[j] = verts[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector<Color> colors; + colors.push_back(random_variation_color); + + RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors); } } + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index b82189e1ee..781f26cc02 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -31,87 +31,378 @@ #ifndef TILE_DATA_EDITORS_H #define TILE_DATA_EDITORS_H +#include "tile_atlas_view.h" + +#include "editor/editor_node.h" +#include "editor/editor_properties.h" + +#include "scene/gui/box_container.h" #include "scene/gui/control.h" +#include "scene/gui/label.h" #include "scene/resources/tile_set.h" -class TileDataEditor : public Control { - GDCLASS(TileDataEditor, Control); +class TileDataEditor : public VBoxContainer { + GDCLASS(TileDataEditor, VBoxContainer); + +private: + void _call_tile_set_changed(); protected: - TileData *tile_data; - String property; + Ref<TileSet> tile_set; + TileData *_get_tile_data(TileMapCell p_cell); + virtual void _tile_set_changed(){}; - TileData *_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile); + static void _bind_methods(); public: - // Edits a TileData property. - void edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property); + void set_tile_set(Ref<TileSet> p_tile_set); + + // Input to handle painting. + virtual Control *get_toolbar() { return nullptr; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){}; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){}; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){}; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event){}; - // Used to draw the value over a tile. - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property){}; + // Used to draw the tile data property value over a tile. + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false){}; }; -class TileDataTextureOffsetEditor : public TileDataEditor { - GDCLASS(TileDataTextureOffsetEditor, TileDataEditor); +class DummyObject : public Object { + GDCLASS(DummyObject, Object) +private: + Map<String, Variant> properties; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + bool has_dummy_property(StringName p_name); + void add_dummy_property(StringName p_name); + void remove_dummy_property(StringName p_name); + void clear_dummy_properties(); }; -class TileDataIntegerEditor : public TileDataEditor { - GDCLASS(TileDataIntegerEditor, TileDataEditor); +class GenericTilePolygonEditor : public VBoxContainer { + GDCLASS(GenericTilePolygonEditor, VBoxContainer); + +private: + Ref<TileSet> tile_set; + LocalVector<Vector<Point2>> polygons; + bool multiple_polygon_mode = false; + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + // UI + int hovered_polygon_index = -1; + int hovered_point_index = -1; + int hovered_segment_index = -1; + Vector2 hovered_segment_point; + + enum DragType { + DRAG_TYPE_NONE, + DRAG_TYPE_DRAG_POINT, + DRAG_TYPE_CREATE_POINT, + DRAG_TYPE_PAN, + }; + DragType drag_type; + int drag_polygon_index; + int drag_point_index; + Vector2 drag_last_pos; + PackedVector2Array drag_old_polygon; + + HBoxContainer *toolbar; + Ref<ButtonGroup> tools_button_group; + Button *button_create; + Button *button_edit; + Button *button_delete; + Button *button_pixel_snap; + MenuButton *button_advanced_menu; + + Vector<Point2> in_creation_polygon; + + Panel *panel; + Control *base_control; + EditorZoomWidget *editor_zoom_widget; + Button *button_center_view; + Vector2 panning; + + Ref<Texture2D> background_texture; + Rect2 background_region; + Vector2 background_offset; + bool background_h_flip; + bool background_v_flip; + bool background_transpose; + Color background_modulate; + + Color polygon_color = Color(1.0, 0.0, 0.0); + + enum AdvancedMenuOption { + RESET_TO_DEFAULT_TILE, + CLEAR_TILE, + }; + + void _base_control_draw(); + void _zoom_changed(); + void _advanced_menu_item_pressed(int p_item_pressed); + void _center_view(); + void _base_control_gui_input(Ref<InputEvent> p_event); + + void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist); + void _snap_to_half_pixel(Point2 &r_point); + void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index); + void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point); + +protected: + void _notification(int p_what); + static void _bind_methods(); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + void set_tile_set(Ref<TileSet> p_tile_set); + void set_background(Ref<Texture2D> p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0)); + + int get_polygon_count(); + int add_polygon(Vector<Point2> p_polygon, int p_index = -1); + void remove_polygon(int p_index); + void clear_polygons(); + void set_polygon(int p_polygon_index, Vector<Point2> p_polygon); + Vector<Point2> get_polygon(int p_polygon_index); + + void set_polygons_color(Color p_color); + void set_multiple_polygon_mode(bool p_multiple_polygon_mode); + + GenericTilePolygonEditor(); }; -class TileDataFloatEditor : public TileDataEditor { - GDCLASS(TileDataFloatEditor, TileDataEditor); +class TileDataDefaultEditor : public TileDataEditor { + GDCLASS(TileDataDefaultEditor, TileDataEditor); + +private: + // Toolbar + HBoxContainer *toolbar = memnew(HBoxContainer); + Button *picker_button; + + // UI + Ref<Texture2D> tile_bool_checked; + Ref<Texture2D> tile_bool_unchecked; + Label *label; + + EditorProperty *property_editor = nullptr; + + // Painting state. + enum DragType { + DRAG_TYPE_NONE = 0, + DRAG_TYPE_PAINT, + DRAG_TYPE_PAINT_RECT, + }; + DragType drag_type = DRAG_TYPE_NONE; + Vector2 drag_start_pos; + Vector2 drag_last_pos; + Map<TileMapCell, Variant> drag_modified; + Variant drag_painted_value; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + +protected: + DummyObject *dummy_object = memnew(DummyObject); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + StringName type; + String property; + void _notification(int p_what); + + virtual Variant _get_painted_value(); + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value); + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual Control *get_toolbar() override { return toolbar; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void setup_property_editor(Variant::Type p_type, String p_property, String p_label = "", Variant p_default_value = Variant()); + + TileDataDefaultEditor(); + ~TileDataDefaultEditor(); }; -class TileDataPositionEditor : public TileDataEditor { - GDCLASS(TileDataPositionEditor, TileDataEditor); +class TileDataTextureOffsetEditor : public TileDataDefaultEditor { + GDCLASS(TileDataTextureOffsetEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataYSortEditor : public TileDataEditor { - GDCLASS(TileDataYSortEditor, TileDataEditor); +class TileDataPositionEditor : public TileDataDefaultEditor { + GDCLASS(TileDataPositionEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataOcclusionShapeEditor : public TileDataEditor { - GDCLASS(TileDataOcclusionShapeEditor, TileDataEditor); +class TileDataYSortEditor : public TileDataDefaultEditor { + GDCLASS(TileDataYSortEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataCollisionShapeEditor : public TileDataEditor { - GDCLASS(TileDataCollisionShapeEditor, TileDataEditor); +class TileDataOcclusionShapeEditor : public TileDataDefaultEditor { + GDCLASS(TileDataOcclusionShapeEditor, TileDataDefaultEditor); + +private: + int occlusion_layer = -1; + + // UI + GenericTilePolygonEditor *polygon_editor; + + void _polygon_changed(PackedVector2Array p_polygon); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); + +public: + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_occlusion_layer(int p_occlusion_layer) { occlusion_layer = p_occlusion_layer; } + + TileDataOcclusionShapeEditor(); +}; + +class TileDataCollisionEditor : public TileDataDefaultEditor { + GDCLASS(TileDataCollisionEditor, TileDataDefaultEditor); + + int physics_layer = -1; + + // UI + GenericTilePolygonEditor *polygon_editor; + DummyObject *dummy_object = memnew(DummyObject); + Map<StringName, EditorProperty *> property_editors; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + void _polygons_changed(); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_physics_layer(int p_physics_layer) { physics_layer = p_physics_layer; } + + TileDataCollisionEditor(); + ~TileDataCollisionEditor(); }; class TileDataTerrainsEditor : public TileDataEditor { GDCLASS(TileDataTerrainsEditor, TileDataEditor); +private: + // Toolbar + HBoxContainer *toolbar = memnew(HBoxContainer); + Button *picker_button; + + // Painting state. + enum DragType { + DRAG_TYPE_NONE = 0, + DRAG_TYPE_PAINT_TERRAIN_SET, + DRAG_TYPE_PAINT_TERRAIN_SET_RECT, + DRAG_TYPE_PAINT_TERRAIN_BITS, + DRAG_TYPE_PAINT_TERRAIN_BITS_RECT, + }; + DragType drag_type = DRAG_TYPE_NONE; + Vector2 drag_start_pos; + Vector2 drag_last_pos; + Map<TileMapCell, Variant> drag_modified; + Variant drag_painted_value; + + // UI + Label *label; + DummyObject *dummy_object = memnew(DummyObject); + EditorPropertyEnum *terrain_set_property_editor = nullptr; + EditorPropertyEnum *terrain_property_editor = nullptr; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + + void _update_terrain_selector(); + +protected: + virtual void _tile_set_changed() override; + + void _notification(int p_what); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual Control *get_toolbar() override { return toolbar; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + TileDataTerrainsEditor(); + ~TileDataTerrainsEditor(); }; -class TileDataNavigationPolygonEditor : public TileDataEditor { - GDCLASS(TileDataNavigationPolygonEditor, TileDataEditor); +class TileDataNavigationEditor : public TileDataDefaultEditor { + GDCLASS(TileDataNavigationEditor, TileDataDefaultEditor); + +private: + int navigation_layer = -1; + PackedVector2Array navigation_polygon; + + // UI + GenericTilePolygonEditor *polygon_editor; + + void _polygon_changed(PackedVector2Array p_polygon); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_navigation_layer(int p_navigation_layer) { navigation_layer = p_navigation_layer; } + + TileDataNavigationEditor(); }; #endif // TILE_DATA_EDITORS_H diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 7ad6462c0e..86bd115ac2 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2989,6 +2989,7 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() { } // Fill in the terrain list. + Vector<Vector<Ref<Texture2D>>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE); for (int terrain_set_index = 0; terrain_set_index < tile_set->get_terrain_sets_count(); terrain_set_index++) { // Add an item for the terrain set. TreeItem *terrain_set_tree_item = terrains_tree->create_item(); @@ -3007,58 +3008,12 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() { terrain_set_tree_item->set_selectable(0, false); for (int terrain_index = 0; terrain_index < tile_set->get_terrains_count(terrain_set_index); terrain_index++) { - // Compute the terrains_tile_pattern used for terrain preview (whenever possible). - TerrainsTilePattern terrains_tile_pattern; - int max_bit_count = -1; - for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[terrain_set_index][terrain_index].front(); E; E = E->next()) { - int count = 0; - for (int i = 0; i < E->get().size(); i++) { - if (int(E->get()[i]) == terrain_index) { - count++; - } - } - if (count > max_bit_count) { - terrains_tile_pattern = E->get(); - max_bit_count = count; - } - } - - // Get the preview. - Ref<Texture2D> icon; - Rect2 region; - if (max_bit_count >= 0) { - double max_probability = -1.0; - for (Set<TileMapCell>::Element *E = per_terrain_terrains_tile_patterns_tiles[terrain_set_index][terrains_tile_pattern].front(); E; E = E->next()) { - Ref<TileSetSource> source = tile_set->get_source(E->get().source_id); - - Ref<TileSetAtlasSource> atlas_source = source; - if (atlas_source.is_valid()) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile)); - if (tile_data->get_probability() > max_probability) { - icon = atlas_source->get_texture(); - region = atlas_source->get_tile_texture_region(E->get().get_atlas_coords()); - max_probability = tile_data->get_probability(); - } - } - } - } else { - Ref<Image> image; - image.instantiate(); - image->create(1, 1, false, Image::FORMAT_RGBA8); - image->set_pixel(0, 0, tile_set->get_terrain_color(terrain_set_index, terrain_index)); - Ref<ImageTexture> image_texture; - image_texture.instantiate(); - image_texture->create_from_image(image); - image_texture->set_size_override(Size2(32, 32) * EDSCALE); - icon = image_texture; - } - // Add the item to the terrain list. TreeItem *terrain_tree_item = terrains_tree->create_item(terrain_set_tree_item); terrain_tree_item->set_text(0, tile_set->get_terrain_name(terrain_set_index, terrain_index)); terrain_tree_item->set_icon_max_width(0, 32 * EDSCALE); - terrain_tree_item->set_icon(0, icon); - terrain_tree_item->set_icon_region(0, region); + terrain_tree_item->set_icon(0, icons[terrain_set_index][terrain_index]); + Dictionary metadata_dict; metadata_dict["terrain_set"] = terrain_set_index; metadata_dict["terrain_id"] = terrain_index; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index f6007afe4e..9d849a0df5 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -357,6 +357,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_bind_methods() { void TileSetAtlasSourceEditor::_inspector_property_selected(String p_property) { selected_property = p_property; _update_atlas_view(); + _update_current_tile_data_editor(); } void TileSetAtlasSourceEditor::_update_tile_id_label() { @@ -398,17 +399,315 @@ void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() { } } +void TileSetAtlasSourceEditor::_update_atlas_source_inspector() { + // Update visibility. + bool visible = tools_button_group->get_pressed_button() == tool_setup_atlas_source_button; + atlas_source_inspector_label->set_visible(visible); + atlas_source_inspector->set_visible(visible); +} + void TileSetAtlasSourceEditor::_update_tile_inspector() { - bool has_atlas_tile_selected = (tools_button_group->get_pressed_button() == tool_select_button) && !selection.is_empty(); + // Update visibility. + if (tools_button_group->get_pressed_button() == tool_select_button) { + if (!selection.is_empty()) { + tile_proxy_object->edit(tile_set_atlas_source, selection); + } + tile_inspector_label->show(); + tile_inspector->set_visible(!selection.is_empty()); + tile_inspector_no_tile_selected_label->set_visible(selection.is_empty()); + } else { + tile_inspector_label->hide(); + tile_inspector->hide(); + tile_inspector_no_tile_selected_label->hide(); + } +} - // Update the proxy object. - if (has_atlas_tile_selected) { - tile_proxy_object->edit(tile_set_atlas_source, selection); +void TileSetAtlasSourceEditor::_update_tile_data_editors() { + String previously_selected; + if (tile_data_editors_tree && tile_data_editors_tree->get_selected()) { + previously_selected = tile_data_editors_tree->get_selected()->get_metadata(0); + } + + tile_data_editors_tree->clear(); + + TreeItem *root = tile_data_editors_tree->create_item(); + + TreeItem *group; +#define ADD_TILE_DATA_EDITOR_GROUP(text) \ + group = tile_data_editors_tree->create_item(root); \ + group->set_custom_bg_color(0, group_color); \ + group->set_selectable(0, false); \ + group->set_disable_folding(true); \ + group->set_text(0, text); + + TreeItem *item; +#define ADD_TILE_DATA_EDITOR(parent, text, property) \ + item = tile_data_editors_tree->create_item(parent); \ + item->set_text(0, text); \ + item->set_metadata(0, property); \ + if (property == previously_selected) { \ + item->select(0); \ + } + + // Theming. + tile_data_editors_tree->add_theme_constant_override("vseparation", 1); + tile_data_editors_tree->add_theme_constant_override("hseparation", 3); + + Color group_color = get_theme_color("prop_category", "Editor"); + + // List of editors. + // --- Rendering --- + ADD_TILE_DATA_EDITOR_GROUP("Rendering"); + + ADD_TILE_DATA_EDITOR(group, "Texture Offset", "texture_offset"); + if (!tile_data_editors.has("texture_offset")) { + TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); + tile_data_texture_offset_editor->hide(); + tile_data_texture_offset_editor->setup_property_editor(Variant::VECTOR2, "texture_offset"); + tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["texture_offset"] = tile_data_texture_offset_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Modulate", "modulate"); + if (!tile_data_editors.has("modulate")) { + TileDataDefaultEditor *tile_data_modulate_editor = memnew(TileDataDefaultEditor()); + tile_data_modulate_editor->hide(); + tile_data_modulate_editor->setup_property_editor(Variant::COLOR, "modulate", "", Color(1.0, 1.0, 1.0, 1.0)); + tile_data_modulate_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_modulate_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["modulate"] = tile_data_modulate_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Z Index", "z_index"); + if (!tile_data_editors.has("z_index")) { + TileDataDefaultEditor *tile_data_z_index_editor = memnew(TileDataDefaultEditor()); + tile_data_z_index_editor->hide(); + tile_data_z_index_editor->setup_property_editor(Variant::INT, "z_index"); + tile_data_z_index_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_z_index_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["z_index"] = tile_data_z_index_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Y Sort Origin", "y_sort_origin"); + if (!tile_data_editors.has("y_sort_origin")) { + TileDataYSortEditor *tile_data_y_sort_editor = memnew(TileDataYSortEditor); + tile_data_y_sort_editor->hide(); + tile_data_y_sort_editor->setup_property_editor(Variant::INT, "y_sort_origin"); + tile_data_y_sort_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_y_sort_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["y_sort_origin"] = tile_data_y_sort_editor; + } + + for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Occlusion Layer %d", i), vformat("occlusion_layer_%d", i)); + if (!tile_data_editors.has(vformat("occlusion_layer_%d", i))) { + TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor()); + tile_data_occlusion_shape_editor->hide(); + tile_data_occlusion_shape_editor->set_occlusion_layer(i); + tile_data_occlusion_shape_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_occlusion_shape_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("occlusion_layer_%d", i)] = tile_data_occlusion_shape_editor; + } + } + for (int i = tile_set->get_occlusion_layers_count(); tile_data_editors.has(vformat("occlusion_layer_%d", i)); i++) { + tile_data_editors[vformat("occlusion_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("occlusion_layer_%d", i)); + } + + // --- Rendering --- + ADD_TILE_DATA_EDITOR(root, "Terrains", "terrain_set"); + if (!tile_data_editors.has("terrain_set")) { + TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor); + tile_data_terrains_editor->hide(); + tile_data_terrains_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_terrains_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["terrain_set"] = tile_data_terrains_editor; + } + + // --- Miscellaneous --- + ADD_TILE_DATA_EDITOR(root, "Probability", "probability"); + if (!tile_data_editors.has("probability")) { + TileDataDefaultEditor *tile_data_probability_editor = memnew(TileDataDefaultEditor()); + tile_data_probability_editor->hide(); + tile_data_probability_editor->setup_property_editor(Variant::FLOAT, "probability", "", 1.0); + tile_data_probability_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_probability_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["probability"] = tile_data_probability_editor; + } + + // --- Physics --- + ADD_TILE_DATA_EDITOR_GROUP("Physics"); + for (int i = 0; i < tile_set->get_physics_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Physics Layer %d", i), vformat("physics_layer_%d", i)); + if (!tile_data_editors.has(vformat("physics_layer_%d", i))) { + TileDataCollisionEditor *tile_data_collision_editor = memnew(TileDataCollisionEditor()); + tile_data_collision_editor->hide(); + tile_data_collision_editor->set_physics_layer(i); + tile_data_collision_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_collision_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("physics_layer_%d", i)] = tile_data_collision_editor; + } + } + for (int i = tile_set->get_physics_layers_count(); tile_data_editors.has(vformat("physics_layer_%d", i)); i++) { + tile_data_editors[vformat("physics_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("physics_layer_%d", i)); + } + + // --- Navigation --- + ADD_TILE_DATA_EDITOR_GROUP("Navigation"); + for (int i = 0; i < tile_set->get_navigation_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Navigation Layer %d", i), vformat("navigation_layer_%d", i)); + if (!tile_data_editors.has(vformat("navigation_layer_%d", i))) { + TileDataNavigationEditor *tile_data_navigation_editor = memnew(TileDataNavigationEditor()); + tile_data_navigation_editor->hide(); + tile_data_navigation_editor->set_navigation_layer(i); + tile_data_navigation_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_navigation_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("navigation_layer_%d", i)] = tile_data_navigation_editor; + } + } + for (int i = tile_set->get_navigation_layers_count(); tile_data_editors.has(vformat("navigation_layer_%d", i)); i++) { + tile_data_editors[vformat("navigation_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("navigation_layer_%d", i)); + } + + // --- Custom Data --- + ADD_TILE_DATA_EDITOR_GROUP("Custom Data"); + for (int i = 0; i < tile_set->get_custom_data_layers_count(); i++) { + if (tile_set->get_custom_data_name(i).is_empty()) { + ADD_TILE_DATA_EDITOR(group, vformat("Custom Data %d", i), vformat("custom_data_%d", i)); + } else { + ADD_TILE_DATA_EDITOR(group, tile_set->get_custom_data_name(i), vformat("custom_data_%d", i)); + } + if (!tile_data_editors.has(vformat("custom_data_%d", i))) { + TileDataDefaultEditor *tile_data_custom_data_editor = memnew(TileDataDefaultEditor()); + tile_data_custom_data_editor->hide(); + tile_data_custom_data_editor->setup_property_editor(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), tile_set->get_custom_data_name(i)); + tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("custom_data_%d", i)] = tile_data_custom_data_editor; + } + } + for (int i = tile_set->get_custom_data_layers_count(); tile_data_editors.has(vformat("custom_data_%d", i)); i++) { + tile_data_editors[vformat("custom_data_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("custom_data_%d", i)); + } + +#undef ADD_TILE_DATA_EDITOR_GROUP +#undef ADD_TILE_DATA_EDITOR + + // Add tile data editors as children. + for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) { + // Tile Data Editor. + TileDataEditor *tile_data_editor = E->get(); + if (!tile_data_editor->is_inside_tree()) { + tile_data_painting_editor_container->add_child(tile_data_editor); + } + tile_data_editor->set_tile_set(tile_set); + + // Toolbar. + Control *toolbar = tile_data_editor->get_toolbar(); + if (!toolbar->is_inside_tree()) { + tool_settings_tile_data_toolbar_container->add_child(toolbar); + } + toolbar->hide(); } // Update visibility. - tile_inspector_label->set_visible(has_atlas_tile_selected); - tile_inspector->set_visible(has_atlas_tile_selected); + bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button; + tile_data_editor_dropdown_button->set_visible(is_visible); + tile_data_editor_dropdown_button->set_text(TTR("Select a property editor")); + tile_data_editors_label->set_visible(is_visible); +} + +void TileSetAtlasSourceEditor::_update_current_tile_data_editor() { + // Find the property to use. + String property; + if (tools_button_group->get_pressed_button() == tool_select_button && tile_inspector->is_visible() && !tile_inspector->get_selected_path().is_empty()) { + Vector<String> components = tile_inspector->get_selected_path().split("/"); + if (components.size() >= 1) { + property = components[0]; + + // Workaround for terrains as they don't have a common first component. + if (property.begins_with("terrains_")) { + property = "terrain_set"; + } + } + } else if (tools_button_group->get_pressed_button() == tool_paint_button && tile_data_editors_tree->get_selected()) { + property = tile_data_editors_tree->get_selected()->get_metadata(0); + tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0)); + } + + // Hide all editors but the current one. + for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) { + E->get()->hide(); + E->get()->get_toolbar()->hide(); + } + if (tile_data_editors.has(property)) { + current_tile_data_editor = tile_data_editors[property]; + } else { + current_tile_data_editor = nullptr; + } + + // Get the correct editor for the TileData's property. + if (current_tile_data_editor) { + current_tile_data_editor_toolbar = current_tile_data_editor->get_toolbar(); + current_property = property; + current_tile_data_editor->set_visible(tools_button_group->get_pressed_button() == tool_paint_button); + current_tile_data_editor_toolbar->set_visible(tools_button_group->get_pressed_button() == tool_paint_button); + } +} + +void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw() { + if (!has_theme_icon("arrow", "OptionButton")) { + return; + } + + RID ci = tile_data_editor_dropdown_button->get_canvas_item(); + Ref<Texture2D> arrow = Control::get_theme_icon("arrow", "OptionButton"); + Color clr = Color(1, 1, 1); + if (get_theme_constant("modulate_arrow")) { + switch (tile_data_editor_dropdown_button->get_draw_mode()) { + case BaseButton::DRAW_PRESSED: + clr = get_theme_color("font_pressed_color"); + break; + case BaseButton::DRAW_HOVER: + clr = get_theme_color("font_hover_color"); + break; + case BaseButton::DRAW_DISABLED: + clr = get_theme_color("font_disabled_color"); + break; + default: + clr = get_theme_color("font_color"); + } + } + + Size2 size = tile_data_editor_dropdown_button->get_size(); + + Point2 ofs; + if (is_layout_rtl()) { + ofs = Point2(get_theme_constant("arrow_margin", "OptionButton"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } else { + ofs = Point2(size.width - arrow->get_width() - get_theme_constant("arrow_margin", "OptionButton"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } + arrow->draw(ci, ofs, clr); +} + +void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_pressed() { + Size2 size = tile_data_editor_dropdown_button->get_size(); + tile_data_editors_popup->set_position(tile_data_editor_dropdown_button->get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); + tile_data_editors_popup->set_size(Size2(size.width, 0)); + tile_data_editors_popup->popup(); +} + +void TileSetAtlasSourceEditor::_tile_data_editors_tree_selected() { + tile_data_editors_popup->call_deferred("hide"); + _update_current_tile_data_editor(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); } void TileSetAtlasSourceEditor::_update_atlas_view() { @@ -467,19 +766,28 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { } void TileSetAtlasSourceEditor::_update_toolbar() { - // Hide all settings. - for (int i = 0; i < tool_settings->get_child_count(); i++) { - Object::cast_to<CanvasItem>(tool_settings->get_child(i))->hide(); - } - - // SHow only the correct settings. - if (tools_button_group->get_pressed_button() == tool_select_button) { - } else if (tools_button_group->get_pressed_button() == tool_add_remove_button) { - tool_settings_vsep->show(); - tools_settings_erase_button->show(); - } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { + // Show the tools and settings. + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->hide(); + } tool_settings_vsep->show(); tools_settings_erase_button->show(); + tool_advanced_menu_buttom->show(); + } else if (tools_button_group->get_pressed_button() == tool_select_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->hide(); + } + tool_settings_vsep->hide(); + tools_settings_erase_button->hide(); + tool_advanced_menu_buttom->hide(); + } else if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->show(); + } + tool_settings_vsep->hide(); + tools_settings_erase_button->hide(); + tool_advanced_menu_buttom->hide(); } } @@ -499,357 +807,336 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Update the hovered coords. hovered_base_tile_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - // Handle the event. - Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid()) { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + // Forward the event to the current tile data editor if we are in the painting mode. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor) { + current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event); + } + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; - Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } else { + // Handle the event. + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - if (drag_type == DRAG_TYPE_NONE) { - if (selection.size() == 1) { - // Change the cursor depending on the hovered thing. - TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { - Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); - Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); - Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); - const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; - const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; - CursorShape cursor_shape = CURSOR_ARROW; - bool can_grow[4]; - for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); - can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; - } - for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; - if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { - cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + + if (drag_type == DRAG_TYPE_NONE) { + if (selection.size() == 1) { + // Change the cursor depending on the hovered thing. + TileSelection selected = selection.front()->get(); + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); + Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); + Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); + const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; + CursorShape cursor_shape = CURSOR_ARROW; + bool can_grow[4]; + for (int i = 0; i < 4; i++) { + can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; - if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { - cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + for (int i = 0; i < 4; i++) { + Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { + cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } + Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { + cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + } } + tile_atlas_control->set_default_cursor_shape(cursor_shape); } - tile_atlas_control->set_default_cursor_shape(cursor_shape); } - } - } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) { - // Create big tile. - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - - Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); - new_rect.size += Vector2i(1, 1); - // Check if the new tile can fit in the new rect. - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { - // Move and resize the tile. - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); - drag_current_tile = new_rect.position; - } - } else if (drag_type == DRAG_TYPE_CREATE_TILES) { - // Create tiles. - last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - - Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); - for (int i = 0; i < line.size(); i++) { - if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) { - tile_set_atlas_source->create_tile(line[i]); - drag_modified_tiles.insert(line[i]); + } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) { + // Create big tile. + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + + Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); + new_rect.size += Vector2i(1, 1); + // Check if the new tile can fit in the new rect. + if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + // Move and resize the tile. + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); + drag_current_tile = new_rect.position; + } + } else if (drag_type == DRAG_TYPE_CREATE_TILES) { + // Create tiles. + last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + + Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); + for (int i = 0; i < line.size(); i++) { + if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) { + tile_set_atlas_source->create_tile(line[i]); + drag_modified_tiles.insert(line[i]); + } } - } - drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); + drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); - } else if (drag_type == DRAG_TYPE_REMOVE_TILES) { - // Remove tiles. - last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + } else if (drag_type == DRAG_TYPE_REMOVE_TILES) { + // Remove tiles. + last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); - for (int i = 0; i < line.size(); i++) { - Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]); - if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { - drag_modified_tiles.insert(base_tile_coords); + Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); + for (int i = 0; i < line.size(); i++) { + Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]); + if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { + drag_modified_tiles.insert(base_tile_coords); + } } - } - drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); - } else if (drag_type == DRAG_TYPE_MOVE_TILE) { - // Move tile. - Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); - coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) { - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); - selection.clear(); - selection.insert({ coords, 0 }); - drag_current_tile = coords; + drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); + } else if (drag_type == DRAG_TYPE_MOVE_TILE) { + // Move tile. + Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); + coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) { + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); + selection.clear(); + selection.insert({ coords, 0 }); + drag_current_tile = coords; - // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; - _update_tile_inspector(); - _update_atlas_view(); - _update_tile_id_label(); - } - } else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) { - // Resizing a tile. - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size); + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; + _update_tile_inspector(); + _update_atlas_view(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } + } else if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) { + if (Vector2(drag_start_mouse_pos).distance_to(tile_atlas_control->get_local_mouse_position()) > 5.0 * EDSCALE) { + drag_type = DRAG_TYPE_NONE; + } + } else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) { + // Resizing a tile. + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size); - Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)); - Rect2i new_rect = old_rect; + Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)); + Rect2i new_rect = old_rect; - if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) { - new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1); - new_rect.size.x = old_rect.get_end().x - new_rect.position.x; - } - if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) { - new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1); - new_rect.size.y = old_rect.get_end().y - new_rect.position.y; - } + if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) { + new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1); + new_rect.size.x = old_rect.get_end().x - new_rect.position.x; + } + if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) { + new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1); + new_rect.size.y = old_rect.get_end().y - new_rect.position.y; + } - if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { - new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y)); - } - if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { - new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1))); - } + if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { + new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y)); + } + if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { + new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1))); + } - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); - selection.clear(); - selection.insert({ new_rect.position, 0 }); - drag_current_tile = new_rect.position; + if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); + selection.clear(); + selection.insert({ new_rect.position, 0 }); + drag_current_tile = new_rect.position; - // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; - _update_tile_inspector(); - _update_atlas_view(); - _update_tile_id_label(); + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; + _update_tile_inspector(); + _update_atlas_view(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } } + + // Redraw for the hovered tile. + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; } - // Redraw for the hovered tile. - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; - } + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + // Left click pressed. + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { + if (tools_settings_erase_button->is_pressed()) { + // Erasing + if (mb->is_ctrl_pressed() || mb->is_shift_pressed()) { + // Remove tiles using rect. + + // Setup the dragging info. + drag_type = DRAG_TYPE_REMOVE_TILES_USING_RECT; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + } else { + // Remove tiles. - Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid()) { - Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); - if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { - if (mb->is_pressed()) { - // Left click pressed. - if (tools_button_group->get_pressed_button() == tool_add_remove_button) { - if (tools_settings_erase_button->is_pressed()) { - // Remove tiles. - - // Setup the dragging info. - drag_type = DRAG_TYPE_REMOVE_TILES; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - - // Remove a first tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - coords = tile_set_atlas_source->get_tile_at_coords(coords); - } - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - drag_modified_tiles.insert(coords); - } - } else { - if (mb->is_shift_pressed()) { - // Create a big tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - // Setup the dragging info, only if we start on an empty tile. - drag_type = DRAG_TYPE_CREATE_BIG_TILE; + // Setup the dragging info. + drag_type = DRAG_TYPE_REMOVE_TILES; drag_start_mouse_pos = mouse_local_pos; drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = coords; - // Create a tile. - tile_set_atlas_source->create_tile(coords); + // Remove a first tile. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + coords = tile_set_atlas_source->get_tile_at_coords(coords); + } + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + drag_modified_tiles.insert(coords); + } } } else { - // Create tiles. - - // Setup the dragging info. - drag_type = DRAG_TYPE_CREATE_TILES; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - - // Create a first tile if needed. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - tile_set_atlas_source->create_tile(coords); - drag_modified_tiles.insert(coords); - } - } - } - } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { - if (tools_settings_erase_button->is_pressed()) { - // Remove tiles using rect. - - // Setup the dragging info. - drag_type = DRAG_TYPE_REMOVE_TILES_USING_RECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - } else { - if (mb->is_shift_pressed()) { - // Create a big tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - // Setup the dragging info, only if we start on an empty tile. - drag_type = DRAG_TYPE_CREATE_BIG_TILE; + // Creating + if (mb->is_shift_pressed()) { + // Create a big tile. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { + // Setup the dragging info, only if we start on an empty tile. + drag_type = DRAG_TYPE_CREATE_BIG_TILE; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = coords; + + // Create a tile. + tile_set_atlas_source->create_tile(coords); + } + } else if (mb->is_ctrl_pressed()) { + // Create tiles using rect. + drag_type = DRAG_TYPE_CREATE_TILES_USING_RECT; drag_start_mouse_pos = mouse_local_pos; drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = coords; + } else { + // Create tiles. - // Create a tile. - tile_set_atlas_source->create_tile(coords); + // Setup the dragging info. + drag_type = DRAG_TYPE_CREATE_TILES; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + + // Create a first tile if needed. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { + tile_set_atlas_source->create_tile(coords); + drag_modified_tiles.insert(coords); + } } - } else { - // Create tiles using rect. - drag_type = DRAG_TYPE_CREATE_TILES_USING_RECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; } - } - } else if (tools_button_group->get_pressed_button() == tool_select_button) { - // Dragging a handle. - drag_type = DRAG_TYPE_NONE; - if (selection.size() == 1) { - TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { - Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); - Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); - const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; - const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; - CursorShape cursor_shape = CURSOR_ARROW; - bool can_grow[4]; - for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); - can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; - } - for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; - if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { - drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2); - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } else if (tools_button_group->get_pressed_button() == tool_select_button) { + // Dragging a handle. + drag_type = DRAG_TYPE_NONE; + if (selection.size() == 1) { + TileSelection selected = selection.front()->get(); + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); + Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); + Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); + const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; + CursorShape cursor_shape = CURSOR_ARROW; + bool can_grow[4]; + for (int i = 0; i < 4; i++) { + can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; - if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { - drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2); - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + for (int i = 0; i < 4; i++) { + Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { + drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2); + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } + Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { + drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2); + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + } } + tile_atlas_control->set_default_cursor_shape(cursor_shape); } - tile_atlas_control->set_default_cursor_shape(cursor_shape); } - } - // Selecting then dragging a tile. - if (drag_type == DRAG_TYPE_NONE) { - TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE }; - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - coords = tile_set_atlas_source->get_tile_at_coords(coords); + // Selecting then dragging a tile. + if (drag_type == DRAG_TYPE_NONE) { + TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE }; + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - selected = { coords, 0 }; + coords = tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + selected = { coords, 0 }; + } } - } - bool shift = mb->is_shift_pressed(); - if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { - // Start move dragging. - drag_type = DRAG_TYPE_MOVE_TILE; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE); - } else { - // Start selection dragging. - drag_type = DRAG_TYPE_RECT_SELECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; + bool shift = mb->is_shift_pressed(); + if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + // Start move dragging. + drag_type = DRAG_TYPE_MOVE_TILE; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE); + } else { + // Start selection dragging. + drag_type = DRAG_TYPE_RECT_SELECT; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + } } } + } else { + // Left click released. + _end_dragging(); } - } else { - // Left click released. - _end_dragging(); - } - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; - } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { - if (mb->is_pressed()) { + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { // Right click pressed. - - TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 }; - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { - selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile); - } - - // Set the selection if needed. - if (selection.size() <= 1) { - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { - undo_redo->create_action(TTR("Select tiles")); - undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array()); - selection.clear(); - selection.insert(selected); - undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); - undo_redo->commit_action(false); - _update_tile_inspector(); - _update_tile_id_label(); - } - } - - // Pops up the correct menu, depending on whether we have a tile or not. - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { - // We have a tile. - menu_option_coords = selected.tile; - menu_option_alternative = 0; - base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); - } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { - // We don't have a tile, but can create one. - menu_option_coords = hovered_base_tile_coords; - menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; - empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_MAY_POPUP_MENU; + drag_start_mouse_pos = tile_atlas_control->get_local_mouse_position(); + } else { + // Right click released. + _end_dragging(); } - } else { - // Right click released. - _end_dragging(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; } - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; } } } @@ -1000,10 +1287,45 @@ void TileSetAtlasSourceEditor::_end_dragging() { } _update_tile_inspector(); _update_tile_id_label(); + _update_current_tile_data_editor(); undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); undo_redo->commit_action(false); - break; - } + } break; + case DRAG_TYPE_MAY_POPUP_MENU: { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 }; + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { + selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile); + } + + // Set the selection if needed. + if (selection.size() <= 1) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { + undo_redo->create_action(TTR("Select tiles")); + undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array()); + selection.clear(); + selection.insert(selected); + undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); + undo_redo->commit_action(false); + _update_tile_inspector(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } + } + + // Pops up the correct menu, depending on whether we have a tile or not. + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + // We have a tile. + menu_option_coords = selected.tile; + menu_option_alternative = 0; + base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { + // We don't have a tile, but can create one. + menu_option_coords = hovered_base_tile_coords; + menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; + empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + } + } break; case DRAG_TYPE_RESIZE_TOP_LEFT: case DRAG_TYPE_RESIZE_TOP: case DRAG_TYPE_RESIZE_TOP_RIGHT: @@ -1166,6 +1488,7 @@ void TileSetAtlasSourceEditor::_set_selection_from_array(Array p_selection) { _update_tile_inspector(); _update_tile_id_label(); _update_atlas_view(); + _update_current_tile_data_editor(); } Array TileSetAtlasSourceEditor::_get_selection_as_array() { @@ -1297,7 +1620,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false); } else { // Draw empty tile, only in add/remove tiles mode. - if (tools_button_group->get_pressed_button() == tool_add_remove_button || tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { Vector2i margins = tile_set_atlas_source->get_margins(); Vector2i separation = tile_set_atlas_source->get_separation(); Vector2i tile_size = tile_set_atlas_source->get_texture_region_size(); @@ -1310,9 +1633,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { } void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { - // Draw the preview of the selected property. - TileDataEditor *tile_data_editor = TileSetEditor::get_singleton()->get_tile_data_editor(selected_property); - if (tile_data_editor && tile_inspector->is_visible_in_tree()) { + if (current_tile_data_editor) { + // Draw the preview of the selected property. for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i coords = tile_set_atlas_source->get_tile_id(i); Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords); @@ -1321,7 +1643,41 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); xform.translate(position); - tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, *tile_set, tile_set_atlas_source_id, coords, 0, selected_property); + if (tools_button_group->get_pressed_button() == tool_select_button && selection.has({ coords, 0 })) { + continue; + } + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + current_tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, cell); + } + + // Draw the selection on top of other. + if (tools_button_group->get_pressed_button() == tool_select_button) { + for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { + if (E->get().alternative != 0) { + continue; + } + Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E->get().tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0); + + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + xform.translate(position); + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(E->get().tile); + cell.alternative_tile = 0; + current_tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, cell, true); + } + } + + // Call the TileData's editor custom draw function. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + current_tile_data_editor->forward_draw_over_atlas(tile_atlas_view, tile_set_atlas_source, tile_atlas_control_unscaled, xform); } } } @@ -1330,6 +1686,19 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In // Update the hovered alternative tile. hovered_alternative_tile_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position()); + // Forward the event to the current tile data editor if we are in the painting mode. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor) { + current_tile_data_editor->forward_painting_alternatives_gui_input(tile_atlas_view, tile_set_atlas_source, p_event); + } + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } + Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { tile_atlas_control->update(); @@ -1425,7 +1794,60 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() { } void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { - //TODO + // Draw the preview of the selected property. + if (current_tile_data_editor) { + // Draw the preview of the currently selected property. + for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = tile_set_atlas_source->get_tile_id(i); + for (int j = 0; j < tile_set_atlas_source->get_alternative_tiles_count(coords); j++) { + int alternative_tile = tile_set_atlas_source->get_alternative_tile_id(coords, j); + if (alternative_tile == 0) { + continue; + } + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2 position = (rect.get_position() + rect.get_end()) / 2; + + Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); + xform.translate(position); + + if (tools_button_group->get_pressed_button() == tool_select_button && selection.has({ coords, alternative_tile })) { + continue; + } + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + current_tile_data_editor->draw_over_tile(alternative_tiles_control_unscaled, xform, cell); + } + } + + // Draw the selection on top of other. + if (tools_button_group->get_pressed_button() == tool_select_button) { + for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) { + if (E->get().alternative == 0) { + continue; + } + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().tile, E->get().alternative); + Vector2 position = (rect.get_position() + rect.get_end()) / 2; + + Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); + xform.translate(position); + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(E->get().tile); + cell.alternative_tile = E->get().alternative; + current_tile_data_editor->draw_over_tile(alternative_tiles_control_unscaled, xform, cell, true); + } + } + + // Call the TileData's editor custom draw function. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + current_tile_data_editor->forward_draw_over_alternatives(tile_atlas_view, tile_set_atlas_source, alternative_tiles_control_unscaled, xform); + } + } } void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() { @@ -1449,15 +1871,23 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited); if (tile_data) { Vector<String> components = String(p_property).split("/", true, 2); - if (components.size() == 2 && components[1] == "shapes_count") { + if (components.size() == 2 && components[1] == "polygons_count") { int layer_index = components[0].trim_prefix("physics_layer_").to_int(); - int new_shapes_count = p_new_value; - int old_shapes_count = tile_data->get(vformat("physics_layer_%d/shapes_count", layer_index)); - if (new_shapes_count < old_shapes_count) { - for (int i = new_shapes_count - 1; i < old_shapes_count; i++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", layer_index, i)); + int new_polygons_count = p_new_value; + int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index)); + if (new_polygons_count < old_polygons_count) { + for (int i = new_polygons_count - 1; i < old_polygons_count; i++) { + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i)); + } + } + } else if (p_property == "terrain_set") { + int current_terrain_set = tile_data->get("terrain_set"); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) { + ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); } } } @@ -1500,7 +1930,10 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource _update_fix_selected_and_hovered_tiles(); _update_tile_id_label(); _update_atlas_view(); + _update_atlas_source_inspector(); _update_tile_inspector(); + _update_tile_data_editors(); + _update_current_tile_data_editor(); } void TileSetAtlasSourceEditor::init_source() { @@ -1616,13 +2049,13 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: + tool_setup_atlas_source_button->set_icon(get_theme_icon("Tools", "EditorIcons")); tool_select_button->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); - tool_add_remove_button->set_icon(get_theme_icon("EditAddRemove", "EditorIcons")); - tool_add_remove_rect_button->set_icon(get_theme_icon("RectangleAddRemove", "EditorIcons")); + tool_paint_button->set_icon(get_theme_icon("CanvasItem", "EditorIcons")); tools_settings_erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons")); - tool_advanced_menu_buttom->set_icon(get_theme_icon("Tools", "EditorIcons")); + tool_advanced_menu_buttom->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons")); resize_handle = get_theme_icon("EditorHandle", "EditorIcons"); resize_handle_disabled = get_theme_icon("EditorHandleDisabled", "EditorIcons"); @@ -1636,7 +2069,10 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { _update_fix_selected_and_hovered_tiles(); _update_tile_id_label(); _update_atlas_view(); + _update_atlas_source_inspector(); _update_tile_inspector(); + _update_tile_data_editors(); + _update_current_tile_data_editor(); tile_set_atlas_source_changed_needs_update = false; } @@ -1675,7 +2111,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // Tile inspector. tile_inspector_label = memnew(Label); tile_inspector_label->set_text(TTR("Tile Properties:")); - tile_inspector_label->hide(); middle_vbox_container->add_child(tile_inspector_label); tile_proxy_object = memnew(AtlasTileProxyObject(this)); @@ -1689,6 +2124,36 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_inspector->connect("property_selected", callable_mp(this, &TileSetAtlasSourceEditor::_inspector_property_selected)); middle_vbox_container->add_child(tile_inspector); + tile_inspector_no_tile_selected_label = memnew(Label); + tile_inspector_no_tile_selected_label->set_align(Label::ALIGN_CENTER); + tile_inspector_no_tile_selected_label->set_text(TTR("No tile selected.")); + middle_vbox_container->add_child(tile_inspector_no_tile_selected_label); + + // Property values palette. + tile_data_editors_popup = memnew(Popup); + + tile_data_editors_label = memnew(Label); + tile_data_editors_label->set_text(TTR("Paint Properties:")); + middle_vbox_container->add_child(tile_data_editors_label); + + tile_data_editor_dropdown_button = memnew(Button); + tile_data_editor_dropdown_button->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw)); + tile_data_editor_dropdown_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_pressed)); + middle_vbox_container->add_child(tile_data_editor_dropdown_button); + tile_data_editor_dropdown_button->add_child(tile_data_editors_popup); + + tile_data_editors_tree = memnew(Tree); + tile_data_editors_tree->set_hide_root(true); + tile_data_editors_tree->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + tile_data_editors_tree->set_h_scroll_enabled(false); + tile_data_editors_tree->set_v_scroll_enabled(false); + tile_data_editors_tree->connect("item_selected", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editors_tree_selected)); + tile_data_editors_popup->add_child(tile_data_editors_tree); + + tile_data_painting_editor_container = memnew(VBoxContainer); + tile_data_painting_editor_container->set_h_size_flags(SIZE_EXPAND_FILL); + middle_vbox_container->add_child(tile_data_painting_editor_container); + // Atlas source inspector. atlas_source_inspector_label = memnew(Label); atlas_source_inspector_label->set_text(TTR("Atlas Properties:")); @@ -1720,46 +2185,40 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // -- Toolbox -- tools_button_group.instantiate(); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_source_inspector).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_data_editors).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_current_tile_data_editor).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar).unbind(1)); toolbox = memnew(HBoxContainer); right_panel->add_child(toolbox); + tool_setup_atlas_source_button = memnew(Button); + tool_setup_atlas_source_button->set_flat(true); + tool_setup_atlas_source_button->set_toggle_mode(true); + tool_setup_atlas_source_button->set_pressed(true); + tool_setup_atlas_source_button->set_button_group(tools_button_group); + tool_setup_atlas_source_button->set_tooltip(TTR("Atlas Setup. Add/Remove tiles tool (use the shift key to create big tiles, control for rectangle editing).")); + toolbox->add_child(tool_setup_atlas_source_button); + tool_select_button = memnew(Button); tool_select_button->set_flat(true); tool_select_button->set_toggle_mode(true); - tool_select_button->set_pressed(true); + tool_select_button->set_pressed(false); tool_select_button->set_button_group(tools_button_group); tool_select_button->set_tooltip(TTR("Select tiles.")); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); toolbox->add_child(tool_select_button); - tool_add_remove_button = memnew(Button); - tool_add_remove_button->set_flat(true); - tool_add_remove_button->set_toggle_mode(true); - tool_add_remove_button->set_button_group(tools_button_group); - tool_add_remove_button->set_tooltip(TTR("Add/Remove tiles tool (use the shift key to create big tiles).")); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); - toolbox->add_child(tool_add_remove_button); - - tool_add_remove_rect_button = memnew(Button); - tool_add_remove_rect_button->set_flat(true); - tool_add_remove_rect_button->set_toggle_mode(true); - tool_add_remove_rect_button->set_button_group(tools_button_group); - tool_add_remove_rect_button->set_tooltip(TTR("Add/Remove tiles rectangle tool (use the shift key to create big tiles).")); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); - toolbox->add_child(tool_add_remove_rect_button); + tool_paint_button = memnew(Button); + tool_paint_button->set_flat(true); + tool_paint_button->set_toggle_mode(true); + tool_paint_button->set_button_group(tools_button_group); + tool_paint_button->set_tooltip(TTR("Paint properties.")); + toolbox->add_child(tool_paint_button); // Tool settings. tool_settings = memnew(HBoxContainer); @@ -1768,6 +2227,9 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tool_settings_vsep = memnew(VSeparator); tool_settings->add_child(tool_settings_vsep); + tool_settings_tile_data_toolbar_container = memnew(HBoxContainer); + tool_settings->add_child(tool_settings_tile_data_toolbar_container); + tools_settings_erase_button = memnew(Button); tools_settings_erase_button->set_flat(true); tools_settings_erase_button->set_toggle_mode(true); @@ -1775,9 +2237,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); - VSeparator *tool_advanced_vsep = memnew(VSeparator); - toolbox->add_child(tool_advanced_vsep); - tool_advanced_menu_buttom = memnew(MenuButton); tool_advanced_menu_buttom->set_flat(true); tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE); @@ -1844,7 +2303,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { alternative_tiles_control_unscaled = memnew(Control); alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE); alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw)); - tile_atlas_view->add_control_over_atlas_tiles(alternative_tiles_control_unscaled, false); + tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false); alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); tile_atlas_view_missing_source_label = memnew(Label); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 70f2cdbe01..dbb0756a16 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -32,6 +32,7 @@ #define TILE_SET_ATLAS_SOURCE_EDITOR_H #include "tile_atlas_view.h" +#include "tile_data_editors.h" #include "editor/editor_node.h" #include "scene/gui/split_container.h" @@ -113,10 +114,27 @@ private: bool tile_set_atlas_source_changed_needs_update = false; + // -- Properties painting -- + VBoxContainer *tile_data_painting_editor_container; + Label *tile_data_editors_label; + Button *tile_data_editor_dropdown_button; + Popup *tile_data_editors_popup; + Tree *tile_data_editors_tree; + void _tile_data_editor_dropdown_button_draw(); + void _tile_data_editor_dropdown_button_pressed(); + + // -- Tile data editors -- + String current_property; + Control *current_tile_data_editor_toolbar = nullptr; + Map<String, TileDataEditor *> tile_data_editors; + TileDataEditor *current_tile_data_editor = nullptr; + void _tile_data_editors_tree_selected(); + // -- Inspector -- AtlasTileProxyObject *tile_proxy_object; Label *tile_inspector_label; EditorInspector *tile_inspector; + Label *tile_inspector_no_tile_selected_label; String selected_property; void _inspector_property_selected(String p_property); @@ -142,6 +160,8 @@ private: DRAG_TYPE_RECT_SELECT, + DRAG_TYPE_MAY_POPUP_MENU, + // Warning: keep in this order. DRAG_TYPE_RESIZE_TOP_LEFT, DRAG_TYPE_RESIZE_TOP, @@ -179,15 +199,16 @@ private: // Tool buttons. Ref<ButtonGroup> tools_button_group; + Button *tool_setup_atlas_source_button; Button *tool_select_button; - Button *tool_add_remove_button; - Button *tool_add_remove_rect_button; + Button *tool_paint_button; Label *tool_tile_id_label; + // Tool settings. HBoxContainer *tool_settings; VSeparator *tool_settings_vsep; + HBoxContainer *tool_settings_tile_data_toolbar_container; Button *tools_settings_erase_button; - MenuButton *tool_advanced_menu_buttom; // Selection. @@ -226,7 +247,10 @@ private: void _update_tile_id_label(); void _update_source_inspector(); void _update_fix_selected_and_hovered_tiles(); + void _update_atlas_source_inspector(); void _update_tile_inspector(); + void _update_tile_data_editors(); + void _update_current_tile_data_editor(); void _update_manage_tile_properties_button(); void _update_atlas_view(); void _update_toolbar(); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index ae5620a4e3..2c2ebd107f 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -347,11 +347,11 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p int old_layer_count = tile_set->get_physics_layers_count(); if (new_layer_count < old_layer_count) { for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shapes_count", physics_layer_index)); - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(physics_layer_index); shape_index++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", physics_layer_index, shape_index)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", physics_layer_index, shape_index)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", physics_layer_index, shape_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", physics_layer_index)); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(physics_layer_index); polygon_index++) { + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", physics_layer_index, polygon_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", physics_layer_index, polygon_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", physics_layer_index, polygon_index)); } } } @@ -359,53 +359,11 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") || (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) { ADD_UNDO(tile_data, "terrain_set"); - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/right_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_right_corner"); + for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(l); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l])); + } } } else if (p_property == "navigation_layers_count") { int new_layer_count = p_new_value; @@ -440,30 +398,6 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::drop_data_fw); } -TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) { - Vector<String> components = String(p_property).split("/", true); - - if (p_property == "z_index") { - return tile_data_integer_editor; - } else if (p_property == "probability") { - return tile_data_float_editor; - } else if (p_property == "y_sort_origin") { - return tile_data_y_sort_editor; - } else if (p_property == "texture_offset") { - return tile_data_texture_offset_editor; - } else if (components.size() >= 1 && components[0].begins_with("occlusion_layer_")) { - return tile_data_occlusion_shape_editor; - } else if (components.size() >= 1 && components[0].begins_with("physics_layer_")) { - return tile_data_collision_shape_editor; - } else if (p_property == "mode" || p_property == "terrain" || (components.size() >= 1 && components[0] == "terrains_peering_bit")) { - return tile_data_terrains_editor; - } else if (components.size() >= 1 && components[0].begins_with("navigation_layer_")) { - return tile_data_navigation_polygon_editor; - } - - return nullptr; -} - void TileSetEditor::edit(Ref<TileSet> p_tile_set) { if (p_tile_set == tile_set) { return; @@ -575,14 +509,4 @@ TileSetEditor::~TileSetEditor() { if (tile_set.is_valid()) { tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); } - - // Delete tile data editors. - memdelete(tile_data_texture_offset_editor); - memdelete(tile_data_y_sort_editor); - memdelete(tile_data_integer_editor); - memdelete(tile_data_float_editor); - memdelete(tile_data_occlusion_shape_editor); - memdelete(tile_data_collision_shape_editor); - memdelete(tile_data_terrains_editor); - memdelete(tile_data_navigation_polygon_editor); } diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index f584c043cc..9e50aca62f 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -33,7 +33,6 @@ #include "scene/gui/box_container.h" #include "scene/resources/tile_set.h" -#include "tile_data_editors.h" #include "tile_set_atlas_source_editor.h" #include "tile_set_scenes_collection_source_editor.h" @@ -54,16 +53,6 @@ private: void _update_atlas_sources_list(int force_selected_id = -1); - // List of tile data editors. - TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); - TileDataYSortEditor *tile_data_y_sort_editor = memnew(TileDataYSortEditor); - TileDataIntegerEditor *tile_data_integer_editor = memnew(TileDataIntegerEditor); - TileDataFloatEditor *tile_data_float_editor = memnew(TileDataFloatEditor); - TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor); - TileDataCollisionShapeEditor *tile_data_collision_shape_editor = memnew(TileDataCollisionShapeEditor); - TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor); - TileDataNavigationPolygonEditor *tile_data_navigation_polygon_editor = memnew(TileDataNavigationPolygonEditor); - // -- Sources management -- Button *sources_delete_button; MenuButton *sources_add_button; @@ -84,7 +73,6 @@ protected: public: _FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; } - TileDataEditor *get_tile_data_editor(String property); void edit(Ref<TileSet> p_tile_set); void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 00750c3032..16d36ad053 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -2315,6 +2315,13 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, _from_node, _from_slot, to_node, to_slot); undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, _from_node, _from_slot, to_node, to_slot); } else { + // Need to setting up Input node properly before committing since `is_port_types_compatible` (calling below) is using `mode` and `shader_type`. + VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsnode.ptr()); + if (input) { + input->set_shader_mode(visual_shader->get_mode()); + input->set_shader_type(visual_shader->get_shader_type()); + } + // Attempting to connect to the first correct port. for (int i = 0; i < vsnode->get_output_port_count(); i++) { if (visual_shader->is_port_types_compatible(vsnode->get_output_port_type(i), input_port_type)) { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2114ca41d3..e63818ef07 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1409,9 +1409,102 @@ void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, List<Pai _fill_path_renames(base_path, new_base_path, p_node, p_renames); } +bool SceneTreeDock::_update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames) { + NodePath root_path_new = p_root_path; + for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) { + if (p_root_path == F->get().first) { + root_path_new = F->get().second; + break; + } + } + + // Goes through all paths to check if it's matching. + for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) { + NodePath rel_path_old = p_root_path.rel_path_to(F->get().first); + + // If old path detected, then it needs to be replaced with the new one. + if (p_node_path == rel_path_old) { + NodePath rel_path_new = F->get().second; + + // If not empty, get new relative path. + if (!rel_path_new.is_empty()) { + rel_path_new = root_path_new.rel_path_to(rel_path_new); + } + + p_node_path = rel_path_new; + return true; + } + + // Update the node itself if it has a valid node path and has not been deleted. + if (p_root_path == F->get().first && p_node_path != NodePath() && F->get().second != NodePath()) { + NodePath abs_path = NodePath(String(root_path_new).plus_file(p_node_path)).simplified(); + NodePath rel_path_new = F->get().second.rel_path_to(abs_path); + + p_node_path = rel_path_new; + return true; + } + } + + return false; +} + +bool SceneTreeDock::_check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List<Pair<NodePath, NodePath>> *p_renames) { + switch (p_variant.get_type()) { + case Variant::NODE_PATH: { + NodePath node_path = p_variant; + if (_update_node_path(p_root_path, node_path, p_renames)) { + p_variant = node_path; + return true; + } + } break; + + case Variant::ARRAY: { + Array a = p_variant; + bool updated = false; + for (int i = 0; i < a.size(); i++) { + Variant value = a[i]; + if (_check_node_path_recursive(p_root_path, value, p_renames)) { + if (!updated) { + a = a.duplicate(); // Need to duplicate for undo-redo to work. + updated = true; + } + a[i] = value; + } + } + if (updated) { + p_variant = a; + return true; + } + } break; + + case Variant::DICTIONARY: { + Dictionary d = p_variant; + bool updated = false; + for (int i = 0; i < d.size(); i++) { + Variant value = d.get_value_at_index(i); + if (_check_node_path_recursive(p_root_path, value, p_renames)) { + if (!updated) { + d = d.duplicate(); // Need to duplicate for undo-redo to work. + updated = true; + } + d[d.get_key_at_index(i)] = value; + } + } + if (updated) { + p_variant = d; + return true; + } + } break; + + default: { + } + } + + return false; +} + void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodePath>> *p_renames, Map<Ref<Animation>, Set<int>> *r_rem_anims) { Map<Ref<Animation>, Set<int>> rem_anims; - if (!r_rem_anims) { r_rem_anims = &rem_anims; } @@ -1424,60 +1517,22 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP return; } - // Renaming node paths used in script instances - if (p_base->get_script_instance()) { - ScriptInstance *si = p_base->get_script_instance(); - - if (si) { - List<PropertyInfo> properties; - si->get_property_list(&properties); - NodePath root_path = p_base->get_path(); - - for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - String propertyname = E->get().name; - Variant p = p_base->get(propertyname); - if (p.get_type() == Variant::NODE_PATH) { - NodePath root_path_new = root_path; - for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) { - if (root_path == F->get().first) { - root_path_new = F->get().second; - break; - } - } - - // Goes through all paths to check if its matching - for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) { - NodePath rel_path_old = root_path.rel_path_to(F->get().first); + // Renaming node paths used in node properties. + List<PropertyInfo> properties; + p_base->get_property_list(&properties); + NodePath base_root_path = p_base->get_path(); - // if old path detected, then it needs to be replaced with the new one - if (p == rel_path_old) { - NodePath rel_path_new = F->get().second; - - // if not empty, get new relative path - if (!rel_path_new.is_empty()) { - rel_path_new = root_path_new.rel_path_to(F->get().second); - } - - editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new); - editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old); - - p_base->set(propertyname, rel_path_new); - break; - } - - // update the node itself if it has a valid node path and has not been deleted - if (root_path == F->get().first && p != NodePath() && F->get().second != NodePath()) { - NodePath abs_path = NodePath(String(root_path).plus_file(p)).simplified(); - NodePath rel_path_new = F->get().second.rel_path_to(abs_path); - - editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new); - editor_data->get_undo_redo().add_undo_property(p_base, propertyname, p); - - p_base->set(propertyname, rel_path_new); - } - } - } - } + for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + if (!(E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) { + continue; + } + String propertyname = E->get().name; + Variant old_variant = p_base->get(propertyname); + Variant updated_variant = old_variant; + if (_check_node_path_recursive(base_root_path, updated_variant, p_renames)) { + editor_data->get_undo_redo().add_do_property(p_base, propertyname, updated_variant); + editor_data->get_undo_redo().add_undo_property(p_base, propertyname, old_variant); + p_base->set(propertyname, updated_variant); } } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index a313337540..d2a68ebd2e 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -247,6 +247,9 @@ class SceneTreeDock : public VBoxContainer { static SceneTreeDock *singleton; static void _update_configuration_warning(); + static bool _update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames); + static bool _check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List<Pair<NodePath, NodePath>> *p_renames); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp index b4ac0d886e..bdbf151393 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.cpp +++ b/modules/gdnative/gdnative_library_editor_plugin.cpp @@ -356,12 +356,12 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { tree->set_column_titles_visible(true); tree->set_columns(4); tree->set_column_expand(0, false); - tree->set_column_min_width(0, int(200 * EDSCALE)); + tree->set_column_custom_minimum_width(0, int(200 * EDSCALE)); tree->set_column_title(0, TTR("Platform")); tree->set_column_title(1, TTR("Dynamic Library")); tree->set_column_title(2, TTR("Dependencies")); tree->set_column_expand(3, false); - tree->set_column_min_width(3, int(110 * EDSCALE)); + tree->set_column_custom_minimum_width(3, int(110 * EDSCALE)); tree->connect("button_pressed", callable_mp(this, &GDNativeLibraryEditor::_on_item_button)); tree->connect("item_collapsed", callable_mp(this, &GDNativeLibraryEditor::_on_item_collapsed)); tree->connect("item_activated", callable_mp(this, &GDNativeLibraryEditor::_on_item_activated)); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 674403905a..a5080f9c41 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5537,6 +5537,8 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> { template <class T> T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { + ERR_FAIL_COND_V(!p_values.size(), T()); + ERR_FAIL_COND_V(p_times.size() != p_values.size(), p_values[0]); //could use binary search, worth it? int idx = -1; for (int i = 0; i < p_times.size(); i++) { diff --git a/modules/pvr/image_compress_pvrtc.cpp b/modules/pvr/image_compress_pvrtc.cpp index f33912cec0..980cac17d3 100644 --- a/modules/pvr/image_compress_pvrtc.cpp +++ b/modules/pvr/image_compress_pvrtc.cpp @@ -43,6 +43,10 @@ static void _compress_pvrtc1_4bpp(Image *p_img) { if (!img->is_size_po2() || img->get_width() != img->get_height()) { make_mipmaps = img->has_mipmaps(); img->resize_to_po2(true); + // Resizing can fail for some formats + if (!img->is_size_po2() || img->get_width() != img->get_height()) { + ERR_FAIL_MSG("Failed to resize the image for compression."); + } } img->convert(Image::FORMAT_RGBA8); if (!img->has_mipmaps() && make_mipmaps) { diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 60392d8f42..f168a5942e 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -912,39 +912,6 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const { return pinfo; } -static const char *op_names[] = { - //comparison - "Are Equal", //OP_EQUAL, - "Are Not Equal", //OP_NOT_EQUAL, - "Less Than", //OP_LESS, - "Less Than or Equal", //OP_LESS_EQUAL, - "Greater Than", //OP_GREATER, - "Greater Than or Equal", //OP_GREATER_EQUAL, - //mathematic - "Add", //OP_ADD, - "Subtract", //OP_SUBTRACT, - "Multiply", //OP_MULTIPLY, - "Divide", //OP_DIVIDE, - "Negate", //OP_NEGATE, - "Positive", //OP_POSITIVE, - "Remainder", //OP_MODULE, - "Concatenate", //OP_STRING_CONCAT, - //bitwise - "Bit Shift Left", //OP_SHIFT_LEFT, - "Bit Shift Right", //OP_SHIFT_RIGHT, - "Bit And", //OP_BIT_AND, - "Bit Or", //OP_BIT_OR, - "Bit Xor", //OP_BIT_XOR, - "Bit Negate", //OP_BIT_NEGATE, - //logic - "And", //OP_AND, - "Or", //OP_OR, - "Xor", //OP_XOR, - "Not", //OP_NOT, - //containment - "In", //OP_IN, -}; - String VisualScriptOperator::get_caption() const { switch (op) { // comparison @@ -1011,6 +978,71 @@ String VisualScriptOperator::get_caption() const { } } +String VisualScriptOperator::get_operator_name(Variant::Operator p_op) { + switch (p_op) { + // comparison + case Variant::OP_EQUAL: + return "Are Equal"; + case Variant::OP_NOT_EQUAL: + return "Are Not Equal"; + case Variant::OP_LESS: + return "Less Than"; + case Variant::OP_LESS_EQUAL: + return "Less Than or Equal"; + case Variant::OP_GREATER: + return "Greater Than"; + case Variant::OP_GREATER_EQUAL: + return "Greater Than or Equal"; + + // mathematic + case Variant::OP_ADD: + return "Add"; + case Variant::OP_SUBTRACT: + return "Subtract"; + case Variant::OP_MULTIPLY: + return "Multiply"; + case Variant::OP_DIVIDE: + return "Divide"; + case Variant::OP_NEGATE: + return "Negate"; + case Variant::OP_POSITIVE: + return "Positive"; + case Variant::OP_MODULE: + return "Remainder"; + + // bitwise + case Variant::OP_SHIFT_LEFT: + return "Bit Shift Left"; + case Variant::OP_SHIFT_RIGHT: + return "Bit Shift Right"; + case Variant::OP_BIT_AND: + return "Bit And"; + case Variant::OP_BIT_OR: + return "Bit Or"; + case Variant::OP_BIT_XOR: + return "Bit Xor"; + case Variant::OP_BIT_NEGATE: + return "Bit Negate"; + + // logic + case Variant::OP_AND: + return "And"; + case Variant::OP_OR: + return "Or"; + case Variant::OP_XOR: + return "Xor"; + case Variant::OP_NOT: + return "Not"; + case Variant::OP_IN: + return "In"; + + default: { + ERR_FAIL_INDEX_V(p_op, Variant::OP_MAX, ""); + return "Unknown Operator"; + } + } +} + void VisualScriptOperator::set_operator(Variant::Operator p_op) { if (op == p_op) { return; @@ -1048,7 +1080,7 @@ void VisualScriptOperator::_bind_methods() { if (i > 0) { types += ","; } - types += op_names[i]; + types += get_operator_name(static_cast<Variant::Operator>(i)); } String argt = "Any"; @@ -1081,9 +1113,9 @@ public: r_error_str = *p_outputs[0]; } else { if (unary) { - r_error_str = String(op_names[op]) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type()); + r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type()); } else { - r_error_str = String(op_names[op]) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type()); + r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type()); } } } diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 551832b002..b599b92b3a 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -227,6 +227,8 @@ public: void set_typed(Variant::Type p_op); Variant::Type get_typed() const; + static String get_operator_name(Variant::Operator p_op); + virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override; VisualScriptOperator(); diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 78f2832770..7bc0d64718 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -89,6 +89,9 @@ <member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain"> When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake. </member> + <member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0"> + The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected. + </member> <member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key"> When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol). </member> diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 27f0f9af6b..af1dc8ff54 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -42,9 +42,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto _is_multiplayer = gd_mp_api; String host = p_url; - String path = "/"; - String scheme = ""; - int port = 80; + String path; + String scheme; + int port = 0; Error err = p_url.parse_url(scheme, host, port, path); ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url); @@ -55,6 +55,9 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto if (port == 0) { port = ssl ? 443 : 80; } + if (path.is_empty()) { + path = "/"; + } return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers); } diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index dfe4471659..9b2d04f14f 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -65,6 +65,10 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ca_chain", "get_ca_chain"); + ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout); + ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout"); + ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); @@ -108,6 +112,15 @@ void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) { ca_chain = p_ca_chain; } +float WebSocketServer::get_handshake_timeout() const { + return handshake_timeout / 1000.0; +} + +void WebSocketServer::set_handshake_timeout(float p_timeout) { + ERR_FAIL_COND(p_timeout <= 0.0); + handshake_timeout = p_timeout * 1000; +} + NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { if (is_listening()) { return CONNECTION_CONNECTED; diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index bc5e591e7b..26864f3085 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -48,6 +48,7 @@ protected: Ref<CryptoKey> private_key; Ref<X509Certificate> ssl_cert; Ref<X509Certificate> ca_chain; + uint32_t handshake_timeout = 3000; public: virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0; @@ -78,6 +79,9 @@ public: Ref<X509Certificate> get_ca_chain() const; void set_ca_chain(Ref<X509Certificate> p_ca_chain); + float get_handshake_timeout() const; + void set_handshake_timeout(float p_timeout); + WebSocketServer(); ~WebSocketServer(); }; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index af1bdb532c..74017fedd7 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -158,6 +158,7 @@ bool WSLClient::_verify_headers(String &r_protocol) { Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); _peer = Ref<WSLPeer>(memnew(WSLPeer)); IPAddress addr; diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 22bb1b6d1a..ccdb6e9292 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -95,8 +95,8 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) { return true; } -Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) { - if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) { +Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout) { + if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) { return ERR_TIMEOUT; } if (use_ssl) { @@ -188,7 +188,7 @@ void WSLServer::poll() { List<Ref<PendingPeer>> remove_peers; for (List<Ref<PendingPeer>>::Element *E = _pending.front(); E; E = E->next()) { Ref<PendingPeer> ppeer = E->get(); - Error err = ppeer->do_handshake(_protocols); + Error err = ppeer->do_handshake(_protocols, handshake_timeout); if (err == ERR_BUSY) { continue; } else if (err != OK) { diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index 39177a16a8..a428c89f4f 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -40,8 +40,6 @@ #include "core/io/stream_peer_tcp.h" #include "core/io/tcp_server.h" -#define WSL_SERVER_TIMEOUT 1000 - class WSLServer : public WebSocketServer { GDCIIMPL(WSLServer, WebSocketServer); @@ -64,7 +62,7 @@ private: CharString response; int response_sent = 0; - Error do_handshake(const Vector<String> p_protocols); + Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout); }; int _in_buf_size = DEF_BUF_SHIFT; diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 51c72e10eb..bf4244eda4 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -450,6 +450,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re } config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); + config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start"); config["gdnativeLibs"] = libs; config["executable"] = p_name; config["args"] = args; @@ -650,6 +651,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), "")); diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js index 6072782875..ba61b14eb7 100644 --- a/platform/javascript/js/engine/config.js +++ b/platform/javascript/js/engine/config.js @@ -91,6 +91,14 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- */ args: [], /** + * When enabled, the game canvas will automatically grab the focus when the engine starts. + * + * @memberof EngineConfig + * @type {boolean} + * @default + */ + focusCanvas: true, + /** * When enabled, this will turn on experimental virtual keyboard support on mobile. * * @memberof EngineConfig @@ -238,6 +246,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- this.persistentPaths = parse('persistentPaths', this.persistentPaths); this.persistentDrops = parse('persistentDrops', this.persistentDrops); this.experimentalVK = parse('experimentalVK', this.experimentalVK); + this.focusCanvas = parse('focusCanvas', this.focusCanvas); this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs); this.fileSizes = parse('fileSizes', this.fileSizes); this.args = parse('args', this.args); @@ -324,6 +333,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused- 'locale': locale, 'persistentDrops': this.persistentDrops, 'virtualKeyboard': this.experimentalVK, + 'focusCanvas': this.focusCanvas, 'onExecute': this.onExecute, 'onExit': function (p_code) { cleanup(); // We always need to call the cleanup callback to free memory. diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js index 7414b8cc47..5aa750757c 100644 --- a/platform/javascript/js/libs/library_godot_os.js +++ b/platform/javascript/js/libs/library_godot_os.js @@ -72,6 +72,9 @@ const GodotConfig = { GodotConfig.persistent_drops = !!p_opts['persistentDrops']; GodotConfig.on_execute = p_opts['onExecute']; GodotConfig.on_exit = p_opts['onExit']; + if (p_opts['focusCanvas']) { + GodotConfig.canvas.focus(); + } }, locate_file: function (file) { diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub index 1751d56e71..8aebd57fd2 100644 --- a/platform/linuxbsd/SCsub +++ b/platform/linuxbsd/SCsub @@ -9,6 +9,7 @@ common_linuxbsd = [ "crash_handler_linuxbsd.cpp", "os_linuxbsd.cpp", "joypad_linux.cpp", + "freedesktop_screensaver.cpp", ] if "x11" in env and env["x11"]: diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 1487210174..eba672ddcb 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -72,6 +72,7 @@ def get_opts(): BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), BoolVariable("use_msan", "Use LLVM compiler memory sanitizer (MSAN)", False), BoolVariable("pulseaudio", "Detect and use PulseAudio", True), + BoolVariable("dbus", "Detect and use D-Bus to handle screensaver", True), BoolVariable("udev", "Use udev for gamepad connection callbacks", True), BoolVariable("x11", "Enable X11 display", True), BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), @@ -347,6 +348,14 @@ def configure(env): else: print("PulseAudio development libraries not found, disabling driver") + if env["dbus"]: + if os.system("pkg-config --exists dbus-1") == 0: # 0 means found + print("Enabling D-Bus") + env.Append(CPPDEFINES=["DBUS_ENABLED"]) + env.ParseConfig("pkg-config --cflags --libs dbus-1") + else: + print("D-Bus development libraries not found, disabling dependent features") + if platform.system() == "Linux": env.Append(CPPDEFINES=["JOYDEV_ENABLED"]) if env["udev"]: diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index d2531cbe50..fd652c0af2 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -121,6 +121,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const { case FEATURE_ICON: case FEATURE_NATIVE_ICON: case FEATURE_SWAP_BUFFERS: +#ifdef DBUS_ENABLED + case FEATURE_KEEP_SCREEN_ON: +#endif return true; default: { } @@ -822,6 +825,26 @@ bool DisplayServerX11::screen_is_touchscreen(int p_screen) const { return DisplayServer::screen_is_touchscreen(p_screen); } +#ifdef DBUS_ENABLED +void DisplayServerX11::screen_set_keep_on(bool p_enable) { + if (screen_is_kept_on() == p_enable) { + return; + } + + if (p_enable) { + screensaver->inhibit(); + } else { + screensaver->uninhibit(); + } + + keep_screen_on = p_enable; +} + +bool DisplayServerX11::screen_is_kept_on() const { + return keep_screen_on; +} +#endif + Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const { _THREAD_SAFE_METHOD_ @@ -4270,6 +4293,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode _update_real_mouse_position(windows[MAIN_WINDOW_ID]); +#ifdef DBUS_ENABLED + screensaver = memnew(FreeDesktopScreenSaver); + screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); +#endif + r_error = OK; } @@ -4334,6 +4362,10 @@ DisplayServerX11::~DisplayServerX11() { if (xmbstring) { memfree(xmbstring); } + +#ifdef DBUS_ENABLED + memdelete(screensaver); +#endif } void DisplayServerX11::register_x11_driver() { diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 594a38d39e..707775a1da 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -55,6 +55,10 @@ #include "platform/linuxbsd/vulkan_context_x11.h" #endif +#if defined(DBUS_ENABLED) +#include "freedesktop_screensaver.h" +#endif + #include <X11/Xcursor/Xcursor.h> #include <X11/Xlib.h> #include <X11/extensions/XInput2.h> @@ -103,6 +107,11 @@ class DisplayServerX11 : public DisplayServer { RenderingDeviceVulkan *rendering_device_vulkan; #endif +#if defined(DBUS_ENABLED) + FreeDesktopScreenSaver *screensaver; + bool keep_screen_on = false; +#endif + struct WindowData { Window x11_window; ::XIC xic; @@ -291,6 +300,11 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; +#if defined(DBUS_ENABLED) + virtual void screen_set_keep_on(bool p_enable); + virtual bool screen_is_kept_on() const; +#endif + virtual Vector<DisplayServer::WindowID> get_window_list() const; virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp new file mode 100644 index 0000000000..23093698ba --- /dev/null +++ b/platform/linuxbsd/freedesktop_screensaver.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* freedesktop_screensaver.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 "freedesktop_screensaver.h" + +#ifdef DBUS_ENABLED + +#include "core/config/project_settings.h" + +#include <dbus/dbus.h> + +#define BUS_OBJECT_NAME "org.freedesktop.ScreenSaver" +#define BUS_OBJECT_PATH "/org/freedesktop/ScreenSaver" +#define BUS_INTERFACE "org.freedesktop.ScreenSaver" + +void FreeDesktopScreenSaver::inhibit() { + if (unsupported) { + return; + } + + DBusError error; + dbus_error_init(&error); + + DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error); + if (dbus_error_is_set(&error)) { + unsupported = true; + return; + } + + String app_name_string = ProjectSettings::get_singleton()->get("application/config/name"); + const char *app_name = app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data(); + + const char *reason = "Running Godot Engine project"; + + DBusMessage *message = dbus_message_new_method_call( + BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE, + "Inhibit"); + dbus_message_append_args( + message, + DBUS_TYPE_STRING, &app_name, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID); + + DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error); + dbus_message_unref(message); + if (dbus_error_is_set(&error)) { + dbus_connection_unref(bus); + unsupported = false; + return; + } + + DBusMessageIter reply_iter; + dbus_message_iter_init(reply, &reply_iter); + dbus_message_iter_get_basic(&reply_iter, &cookie); + print_verbose("FreeDesktopScreenSaver: Acquired screensaver inhibition cookie: " + uitos(cookie)); + + dbus_message_unref(reply); + dbus_connection_unref(bus); +} + +void FreeDesktopScreenSaver::uninhibit() { + if (unsupported) { + return; + } + + DBusError error; + dbus_error_init(&error); + + DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error); + if (dbus_error_is_set(&error)) { + unsupported = true; + return; + } + + DBusMessage *message = dbus_message_new_method_call( + BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE, + "UnInhibit"); + dbus_message_append_args( + message, + DBUS_TYPE_UINT32, &cookie, + DBUS_TYPE_INVALID); + + DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error); + if (dbus_error_is_set(&error)) { + dbus_connection_unref(bus); + unsupported = true; + return; + } + + print_verbose("FreeDesktopScreenSaver: Released screensaver inhibition cookie: " + uitos(cookie)); + + dbus_message_unref(message); + dbus_message_unref(reply); + dbus_connection_unref(bus); +} + +#endif // DBUS_ENABLED diff --git a/platform/linuxbsd/freedesktop_screensaver.h b/platform/linuxbsd/freedesktop_screensaver.h new file mode 100644 index 0000000000..f27e60fce4 --- /dev/null +++ b/platform/linuxbsd/freedesktop_screensaver.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* freedesktop_screensaver.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. */ +/*************************************************************************/ + +#ifdef DBUS_ENABLED + +#include <dbus/dbus.h> +#include <stdint.h> + +class FreeDesktopScreenSaver { +private: + uint32_t cookie = 0; + bool unsupported = false; + +public: + FreeDesktopScreenSaver() {} + void inhibit(); + void uninhibit(); +}; + +#endif // DBUS_ENABLED diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 03093ab6dd..10f953f2ec 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -310,7 +310,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p args.push_back(p_path); #ifndef WINDOWS_ENABLED args.push_back("-out"); - args.push_back(p_path); + args.push_back(p_path + "_signed"); #endif String str; @@ -326,6 +326,16 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p return FAILED; } +#ifndef WINDOWS_ENABLED + DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); + + err = tmp_dir->remove(p_path); + ERR_FAIL_COND_V(err != OK, err); + + err = tmp_dir->rename(p_path + "_signed", p_path); + ERR_FAIL_COND_V(err != OK, err); +#endif + return OK; } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index d7404ff479..bf7bb12dbe 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -451,7 +451,7 @@ void GPUParticles2D::_notification(int p_what) { RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid); #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); } #endif diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index a7613dc009..e7cef965fe 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -35,7 +35,7 @@ void RemoteTransform2D::_update_cache() { cache = ObjectID(); if (has_node(remote_node)) { Node *node = get_node(remote_node); - if (!node || this == node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + if (!node || this == node || node->is_ancestor_of(this) || this->is_ancestor_of(node)) { return; } diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 15c2da3ae0..4bf20c89c5 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -55,7 +55,7 @@ private: RID probe; float intensity = 1.0; float max_distance = 0.0; - Vector3 extents = Vector3(1, 1, 1); + Vector3 extents = Vector3(10, 10, 10); Vector3 origin_offset = Vector3(0, 0, 0); bool box_projection = false; bool enable_shadows = false; diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index a7b3a6f1ec..d5fb1fa6ab 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -34,7 +34,7 @@ void RemoteTransform3D::_update_cache() { cache = ObjectID(); if (has_node(remote_node)) { Node *node = get_node(remote_node); - if (!node || this == node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + if (!node || this == node || node->is_ancestor_of(this) || this->is_ancestor_of(node)) { return; } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 1bf841be04..1e121ab6e5 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -525,7 +525,7 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -569,7 +569,7 @@ void LiveEditor::_node_call_func(int p_id, const StringName &p_method, VARIANT_A for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -652,7 +652,7 @@ void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_typ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -696,7 +696,7 @@ void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_p for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -736,7 +736,7 @@ void LiveEditor::_remove_node_func(const NodePath &p_at) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -772,7 +772,7 @@ void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_kee Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -811,7 +811,7 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -862,7 +862,7 @@ void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_ for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } @@ -901,7 +901,7 @@ void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { Node *n = F->get(); - if (base && !base->is_a_parent_of(n)) { + if (base && !base->is_ancestor_of(n)) { continue; } diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 66155958cf..c1ae0479f5 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -391,7 +391,7 @@ bool BaseButton::_is_focus_owner_in_shorcut_context() const { Control *vp_focus = get_focus_owner(); // If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it. - return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_a_parent_of(vp_focus)); + return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus)); } void BaseButton::_bind_methods() { diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 595d712eb8..bcc273114b 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -49,9 +49,14 @@ Size2 Button::get_minimum_size() const { if (!_icon.is_null()) { minsize.height = MAX(minsize.height, _icon->get_height()); - minsize.width += _icon->get_width(); - if (xl_text != "") { - minsize.width += get_theme_constant("hseparation"); + + if (icon_align != ALIGN_CENTER) { + minsize.width += _icon->get_width(); + if (xl_text != "") { + minsize.width += get_theme_constant("hseparation"); + } + } else { + minsize.width = MAX(minsize.width, _icon->get_width()); } } } @@ -202,6 +207,21 @@ void Button::_notification(int p_what) { } Rect2 icon_region = Rect2(); + TextAlign icon_align_rtl_checked = icon_align; + TextAlign align_rtl_checked = align; + // Swap icon and text alignment sides if right-to-left layout is set. + if (rtl) { + if (icon_align == ALIGN_RIGHT) { + icon_align_rtl_checked = ALIGN_LEFT; + } else if (icon_align == ALIGN_LEFT) { + icon_align_rtl_checked = ALIGN_RIGHT; + } + if (align == ALIGN_RIGHT) { + align_rtl_checked = ALIGN_LEFT; + } else if (align == ALIGN_LEFT) { + align_rtl_checked = ALIGN_RIGHT; + } + } if (!_icon.is_null()) { int valign = size.height - style->get_minimum_size().y; if (is_disabled()) { @@ -209,20 +229,27 @@ void Button::_notification(int p_what) { } float icon_ofs_region = 0.0; - if (rtl) { - if (_internal_margin[SIDE_RIGHT] > 0) { - icon_ofs_region = _internal_margin[SIDE_RIGHT] + get_theme_constant("hseparation"); - } - } else { + Point2 style_offset; + Size2 icon_size = _icon->get_size(); + if (icon_align_rtl_checked == ALIGN_LEFT) { + style_offset.x = style->get_margin(SIDE_LEFT); if (_internal_margin[SIDE_LEFT] > 0) { icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); } + } else if (icon_align_rtl_checked == ALIGN_CENTER) { + style_offset.x = 0.0; + } else if (icon_align_rtl_checked == ALIGN_RIGHT) { + style_offset.x = -style->get_margin(SIDE_RIGHT); + if (_internal_margin[SIDE_RIGHT] > 0) { + icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); + } } + style_offset.y = style->get_margin(SIDE_TOP); if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; _size.width -= get_theme_constant("hseparation") + icon_ofs_region; - if (!clip_text) { + if (!clip_text && icon_align_rtl_checked != ALIGN_CENTER) { _size.width -= text_buf->get_size().width; } float icon_width = _icon->get_width() * _size.height / _icon->get_height(); @@ -233,21 +260,26 @@ void Button::_notification(int p_what) { icon_height = _icon->get_height() * icon_width / _icon->get_width(); } - if (rtl) { - icon_region = Rect2(Point2(size.width - (icon_ofs_region + icon_width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); - } else { - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); - } + icon_size = Size2(icon_width, icon_height); + } + + if (icon_align_rtl_checked == ALIGN_LEFT) { + icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size); + } else if (icon_align_rtl_checked == ALIGN_CENTER) { + icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), Math::floor((valign - icon_size.y) * 0.5)), icon_size); } else { - if (rtl) { - icon_region = Rect2(Point2(size.width - (icon_ofs_region + _icon->get_size().width + style->get_margin(SIDE_RIGHT)), style->get_margin(SIDE_TOP) + Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); - } else { - icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), _icon->get_size()); - } + icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, Math::floor((valign - icon_size.y) * 0.5)), icon_size); + } + + if (icon_region.size.width > 0) { + draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); } } Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant("hseparation"), 0) : Point2(); + if (align_rtl_checked == ALIGN_CENTER && icon_align_rtl_checked == ALIGN_CENTER) { + icon_ofs.x = 0.0; + } int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; text_buf->set_width(clip_text ? text_clip : -1); @@ -262,20 +294,15 @@ void Button::_notification(int p_what) { Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; - switch (align) { + switch (align_rtl_checked) { case ALIGN_LEFT: { - if (rtl) { - if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); - } else { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; - } + if (icon_align_rtl_checked != ALIGN_LEFT) { + icon_ofs.x = 0.0; + } + if (_internal_margin[SIDE_LEFT] > 0) { + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); } else { - if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; - } + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; } text_ofs.y += style->get_offset().y; } break; @@ -283,31 +310,24 @@ void Button::_notification(int p_what) { if (text_ofs.x < 0) { text_ofs.x = 0; } - text_ofs += icon_ofs; + if (icon_align_rtl_checked == ALIGN_LEFT) { + text_ofs += icon_ofs; + } text_ofs += style->get_offset(); } break; case ALIGN_RIGHT: { - if (rtl) { - if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; - } + if (_internal_margin[SIDE_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); } else { - if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant("hseparation"); - } else { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; - } + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; } text_ofs.y += style->get_offset().y; + if (icon_align_rtl_checked == ALIGN_RIGHT) { + text_ofs.x -= icon_ofs.x; + } } break; } - if (rtl) { - text_ofs.x -= icon_ofs.x; - } - Color font_outline_color = get_theme_color("font_outline_color"); int outline_size = get_theme_constant("outline_size"); if (outline_size > 0 && font_outline_color.a > 0) { @@ -315,10 +335,6 @@ void Button::_notification(int p_what) { } text_buf->draw(ci, text_ofs, color); - - if (!_icon.is_null() && icon_region.size.width > 0) { - draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon); - } } break; } } @@ -457,6 +473,16 @@ Button::TextAlign Button::get_text_align() const { return align; } +void Button::set_icon_align(TextAlign p_align) { + icon_align = p_align; + minimum_size_changed(); + update(); +} + +Button::TextAlign Button::get_icon_align() const { + return icon_align; +} + bool Button::_set(const StringName &p_name, const Variant &p_value) { String str = p_name; if (str.begins_with("opentype_features/")) { @@ -519,14 +545,16 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); - ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); - ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); + ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); ClassDB::bind_method(D_METHOD("set_text_align", "align"), &Button::set_text_align); ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align); - ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat); + ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align); + ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align); + ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -539,6 +567,7 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_align", "get_icon_align"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); } diff --git a/scene/gui/button.h b/scene/gui/button.h index d968f63f51..253d079680 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -58,6 +58,7 @@ private: bool expand_icon = false; bool clip_text = false; TextAlign align = ALIGN_CENTER; + TextAlign icon_align = ALIGN_LEFT; float _internal_margin[4] = {}; void _shape(); @@ -102,6 +103,9 @@ public: void set_text_align(TextAlign p_align); TextAlign get_text_align() const; + void set_icon_align(TextAlign p_align); + TextAlign get_icon_align() const; + Button(const String &p_text = String()); ~Button(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 0ae1bb04b7..1bfdff1134 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1068,7 +1068,7 @@ Rect2 Control::get_parent_anchorable_rect() const { } else { #ifdef TOOLS_ENABLED Node *edited_root = get_tree()->get_edited_scene_root(); - if (edited_root && (this == edited_root || edited_root->is_a_parent_of(this))) { + if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) { parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); } else { parent_rect = get_viewport()->get_visible_rect(); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 93f1fe9e8e..836bffdf46 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -757,7 +757,7 @@ void GraphNode::_connpos_update() { continue; } - Size2i size = c->get_combined_minimum_size(); + Size2i size = c->get_rect().size; int y = sb->get_margin(SIDE_TOP) + vofs; int h = size.y; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 95445bdb7e..177f146b6a 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -235,13 +235,13 @@ void ScrollContainer::_update_scrollbar_position() { } void ScrollContainer::_gui_focus_changed(Control *p_control) { - if (follow_focus && is_a_parent_of(p_control)) { + if (follow_focus && is_ancestor_of(p_control)) { ensure_control_visible(p_control); } } void ScrollContainer::ensure_control_visible(Control *p_control) { - ERR_FAIL_COND_MSG(!is_a_parent_of(p_control), "Must be a parent of the control."); + ERR_FAIL_COND_MSG(!is_ancestor_of(p_control), "Must be an ancestor of the control."); Rect2 global_rect = get_global_rect(); Rect2 other_rect = p_control->get_global_rect(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f677b3592a..aac15cd9a5 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -489,11 +489,11 @@ TreeItem *TreeItem::create_child(int p_idx) { return ti; } -Tree *TreeItem::get_tree() { +Tree *TreeItem::get_tree() const { return tree; } -TreeItem *TreeItem::get_next() { +TreeItem *TreeItem::get_next() const { return next; } @@ -516,11 +516,11 @@ TreeItem *TreeItem::get_prev() { return prev; } -TreeItem *TreeItem::get_parent() { +TreeItem *TreeItem::get_parent() const { return parent; } -TreeItem *TreeItem::get_first_child() { +TreeItem *TreeItem::get_first_child() const { return first_child; } @@ -953,6 +953,53 @@ bool TreeItem::is_folding_disabled() const { return disable_folding; } +Size2 TreeItem::get_minimum_size(int p_column) { + ERR_FAIL_INDEX_V(p_column, cells.size(), Size2()); + Tree *tree = get_tree(); + ERR_FAIL_COND_V(!tree, Size2()); + + Size2 size; + + // Default offset? + //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin; + + // Text. + const TreeItem::Cell &cell = cells[p_column]; + if (!cell.text.is_empty()) { + if (cell.dirty) { + tree->update_item_cell(this, p_column); + } + Size2 text_size = cell.text_buf->get_size(); + size.width += text_size.width; + size.height = MAX(size.height, text_size.height); + } + + // Icon. + if (cell.icon.is_valid()) { + Size2i icon_size = cell.get_icon_size(); + if (cell.icon_max_w > 0 && icon_size.width > cell.icon_max_w) { + icon_size.width = cell.icon_max_w; + } + size.width += icon_size.width + tree->cache.hseparation; + size.height = MAX(size.height, icon_size.height); + } + + // Buttons. + for (int i = 0; i < cell.buttons.size(); i++) { + Ref<Texture2D> texture = cell.buttons[i].texture; + if (texture.is_valid()) { + Size2 button_size = texture->get_size() + tree->cache.button_pressed->get_minimum_size(); + size.width += button_size.width; + size.height = MAX(size.height, button_size.height); + } + } + if (cell.buttons.size() >= 2) { + size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; + } + + return size; +} + Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -3276,7 +3323,7 @@ Size2 Tree::get_internal_min_size() const { size.height += get_item_height(root); } for (int i = 0; i < columns.size(); i++) { - size.width += columns[i].min_width; + size.width += get_column_minimum_width(i); } return size; @@ -3300,26 +3347,38 @@ void Tree::update_scrollbars() { h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - Size2 min = get_internal_min_size(); + Size2 internal_min_size = get_internal_min_size(); - if (min.height < size.height - hmin.height) { - v_scroll->hide(); - cache.offset.y = 0; - } else { + bool display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) > size.height; + bool display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) > size.width; + for (int i = 0; i < 2; i++) { + // Check twice, as both values are dependent on each other. + if (display_hscroll) { + display_vscroll = internal_min_size.height + cache.bg->get_margin(SIDE_TOP) + hmin.height > size.height; + } + if (display_vscroll) { + display_hscroll = internal_min_size.width + cache.bg->get_margin(SIDE_LEFT) + vmin.width > size.width; + } + } + + if (display_vscroll) { v_scroll->show(); - v_scroll->set_max(min.height); + v_scroll->set_max(internal_min_size.height); v_scroll->set_page(size.height - hmin.height - tbh); cache.offset.y = v_scroll->get_value(); + } else { + v_scroll->hide(); + cache.offset.y = 0; } - if (min.width < size.width - vmin.width) { - h_scroll->hide(); - cache.offset.x = 0; - } else { + if (display_hscroll) { h_scroll->show(); - h_scroll->set_max(min.width); + h_scroll->set_max(internal_min_size.width); h_scroll->set_page(size.width - vmin.width); cache.offset.x = h_scroll->get_value(); + } else { + h_scroll->hide(); + cache.offset.x = 0; } } @@ -3445,7 +3504,7 @@ void Tree::_notification(int p_what) { draw_ofs.y += tbh; draw_size.y -= tbh; - if (root) { + if (root && get_size().x > 0 && get_size().y > 0) { draw_item(Point2(), draw_ofs, draw_size, root); } @@ -3513,7 +3572,17 @@ void Tree::_update_all() { } Size2 Tree::get_minimum_size() const { - return Size2(1, 1); + if (h_scroll_enabled && v_scroll_enabled) { + return Size2(); + } else { + Vector2 min_size = get_internal_min_size(); + Ref<StyleBox> bg = cache.bg; + if (bg.is_valid()) { + min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); + min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); + } + return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y); + } } TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { @@ -3541,11 +3610,11 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { return ti; } -TreeItem *Tree::get_root() { +TreeItem *Tree::get_root() const { return root; } -TreeItem *Tree::get_last_item() { +TreeItem *Tree::get_last_item() const { TreeItem *last = root; while (last) { @@ -3675,13 +3744,13 @@ bool Tree::is_root_hidden() const { return hide_root; } -void Tree::set_column_min_width(int p_column, int p_min_width) { +void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) { ERR_FAIL_INDEX(p_column, columns.size()); - if (p_min_width < 1) { + if (p_min_width < 0) { return; } - columns.write[p_column].min_width = p_min_width; + columns.write[p_column].custom_min_width = p_min_width; update(); } @@ -3748,44 +3817,82 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { return nullptr; } -int Tree::get_column_width(int p_column) const { +int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - if (!columns[p_column].expand) { - return columns[p_column].min_width; - } + if (columns[p_column].custom_min_width != 0) { + return columns[p_column].custom_min_width; + } else { + int depth = 0; + int min_width = 0; + TreeItem *next; + for (TreeItem *item = get_root(); item; item = next) { + next = item->get_next_visible(); + // Compute the depth in tree. + if (next && p_column == 0) { + if (next->get_parent() == item) { + depth += 1; + } else { + TreeItem *common_parent = item->get_parent(); + while (common_parent != next->get_parent()) { + common_parent = common_parent->get_parent(); + depth -= 1; + } + } + } - int expand_area = get_size().width; + // Get the item minimum size. + Size2 item_size = item->get_minimum_size(p_column); + if (p_column == 0) { + item_size.width += cache.item_margin * depth; + } + min_width = MAX(min_width, item_size.width); + } + return min_width; + } +} - Ref<StyleBox> bg = cache.bg; +int Tree::get_column_width(int p_column) const { + ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - if (bg.is_valid()) { - expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); - } + if (columns[p_column].expand) { + int expand_area = get_size().width; - if (v_scroll->is_visible_in_tree()) { - expand_area -= v_scroll->get_combined_minimum_size().width; - } + Ref<StyleBox> bg = cache.bg; - int expanding_columns = 0; - int expanding_total = 0; + if (bg.is_valid()) { + expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); + } - for (int i = 0; i < columns.size(); i++) { - if (!columns[i].expand) { - expand_area -= columns[i].min_width; - } else { - expanding_total += columns[i].min_width; - expanding_columns++; + if (v_scroll->is_visible_in_tree()) { + expand_area -= v_scroll->get_combined_minimum_size().width; } - } - if (expand_area < expanding_total) { - return columns[p_column].min_width; - } + int expanding_columns = 0; + int expanding_total = 0; - ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen + for (int i = 0; i < columns.size(); i++) { + if (!columns[i].expand) { + expand_area -= get_column_minimum_width(i); + } else { + expanding_total += get_column_minimum_width(i); + expanding_columns++; + } + } + + if (expand_area < expanding_total) { + return get_column_minimum_width(p_column); + } - return expand_area * columns[p_column].min_width / expanding_total; + ERR_FAIL_COND_V(expanding_columns == 0, -1); // shouldn't happen + if (expanding_total == 0) { + return 0; + } else { + return expand_area * get_column_minimum_width(p_column) / expanding_total; + } + } else { + return get_column_minimum_width(p_column); + } } void Tree::propagate_set_columns(TreeItem *p_item) { @@ -4047,6 +4154,24 @@ void Tree::scroll_to_item(TreeItem *p_item) { } } +void Tree::set_h_scroll_enabled(bool p_enable) { + h_scroll_enabled = p_enable; + minimum_size_changed(); +} + +bool Tree::is_h_scroll_enabled() const { + return h_scroll_enabled; +} + +void Tree::set_v_scroll_enabled(bool p_enable) { + v_scroll_enabled = p_enable; + minimum_size_changed(); +} + +bool Tree::is_v_scroll_enabled() const { + return v_scroll_enabled; +} + TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) { TreeItem *from = p_at; TreeItem *loop = nullptr; // Safe-guard against infinite loop. @@ -4422,7 +4547,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root); - ClassDB::bind_method(D_METHOD("set_column_min_width", "column", "min_width"), &Tree::set_column_min_width); + ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width); ClassDB::bind_method(D_METHOD("set_column_expand", "column", "expand"), &Tree::set_column_expand); ClassDB::bind_method(D_METHOD("get_column_width", "column"), &Tree::get_column_width); @@ -4468,6 +4593,12 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll); ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item); + ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled); + ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled); + + ClassDB::bind_method(D_METHOD("set_v_scroll_enabled", "h_scroll"), &Tree::set_v_scroll_enabled); + ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &Tree::is_v_scroll_enabled); + ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding); ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden); @@ -4487,6 +4618,8 @@ void Tree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden"); ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_h_scroll_enabled", "is_h_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_v_scroll_enabled", "is_v_scroll_enabled"); ADD_SIGNAL(MethodInfo("item_selected")); ADD_SIGNAL(MethodInfo("cell_selected")); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 42dd27cb55..fd5fcd7838 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -315,16 +315,18 @@ public: void set_disable_folding(bool p_disable); bool is_folding_disabled() const; + Size2 get_minimum_size(int p_column); + /* Item manipulation */ TreeItem *create_child(int p_idx = -1); - Tree *get_tree(); + Tree *get_tree() const; TreeItem *get_prev(); - TreeItem *get_next(); - TreeItem *get_parent(); - TreeItem *get_first_child(); + TreeItem *get_next() const; + TreeItem *get_parent() const; + TreeItem *get_first_child() const; TreeItem *get_prev_visible(bool p_wrap = false); TreeItem *get_next_visible(bool p_wrap = false); @@ -408,7 +410,7 @@ private: int drop_mode_flags = 0; struct ColumnInfo { - int min_width = 1; + int custom_min_width = 0; bool expand = true; String title; Ref<TextLine> text_buf; @@ -545,6 +547,8 @@ private: void _scroll_moved(float p_value); HScrollBar *h_scroll; VScrollBar *v_scroll; + bool h_scroll_enabled = true; + bool v_scroll_enabled = true; Size2 get_internal_min_size() const; void update_cache(); @@ -623,11 +627,12 @@ public: void clear(); TreeItem *create_item(TreeItem *p_parent = nullptr, int p_idx = -1); - TreeItem *get_root(); - TreeItem *get_last_item(); + TreeItem *get_root() const; + TreeItem *get_last_item() const; - void set_column_min_width(int p_column, int p_min_width); + void set_column_custom_minimum_width(int p_column, int p_min_width); void set_column_expand(int p_column, bool p_expand); + int get_column_minimum_width(int p_column) const; int get_column_width(int p_column) const; void set_hide_root(bool p_enabled); @@ -679,6 +684,10 @@ public: Point2 get_scroll() const; void scroll_to_item(TreeItem *p_item); + void set_h_scroll_enabled(bool p_enable); + bool is_h_scroll_enabled() const; + void set_v_scroll_enabled(bool p_enable); + bool is_v_scroll_enabled() const; void set_cursor_can_exit_tree(bool p_enable); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index baf185c847..9479b1339d 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1041,7 +1041,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) { ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent #ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(p_child->is_a_parent_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); + ERR_FAIL_COND_MSG(p_child->is_ancestor_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); #endif ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); @@ -1284,7 +1284,7 @@ Node *Node::find_parent(const String &p_mask) const { return nullptr; } -bool Node::is_a_parent_of(const Node *p_node) const { +bool Node::is_ancestor_of(const Node *p_node) const { ERR_FAIL_NULL_V(p_node, false); Node *p = p_node->data.parent; while (p) { @@ -1749,7 +1749,7 @@ String Node::get_editor_description() const { void Node::set_editable_instance(Node *p_node, bool p_editable) { ERR_FAIL_NULL(p_node); - ERR_FAIL_COND(!is_a_parent_of(p_node)); + ERR_FAIL_COND(!is_ancestor_of(p_node)); if (!p_editable) { p_node->data.editable_instance = false; // Avoid this flag being needlessly saved; @@ -1764,13 +1764,13 @@ bool Node::is_editable_instance(const Node *p_node) const { if (!p_node) { return false; // Easier, null is never editable. :) } - ERR_FAIL_COND_V(!is_a_parent_of(p_node), false); + ERR_FAIL_COND_V(!is_ancestor_of(p_node), false); return p_node->data.editable_instance; } Node *Node::get_deepest_editable_node(Node *p_start_node) const { ERR_FAIL_NULL_V(p_start_node, nullptr); - ERR_FAIL_COND_V(!is_a_parent_of(p_start_node), p_start_node); + ERR_FAIL_COND_V(!is_ancestor_of(p_start_node), p_start_node); Node const *iterated_item = p_start_node; Node *node = p_start_node; @@ -2074,7 +2074,7 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc // because re-targeting of connections from some descendant to another is not possible // if the emitter node comes later in tree order than the receiver void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { - if ((this != p_original) && !(p_original->is_a_parent_of(this))) { + if ((this != p_original) && !(p_original->is_ancestor_of(this))) { return; } @@ -2467,7 +2467,7 @@ void Node::update_configuration_warnings() { if (!is_inside_tree()) { return; } - if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { get_tree()->emit_signal(SceneStringNames::get_singleton()->node_configuration_warning_changed, this); } #endif @@ -2514,7 +2514,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); ClassDB::bind_method(D_METHOD("is_inside_tree"), &Node::is_inside_tree); - ClassDB::bind_method(D_METHOD("is_a_parent_of", "node"), &Node::is_a_parent_of); + ClassDB::bind_method(D_METHOD("is_ancestor_of", "node"), &Node::is_ancestor_of); ClassDB::bind_method(D_METHOD("is_greater_than", "node"), &Node::is_greater_than); ClassDB::bind_method(D_METHOD("get_path"), &Node::get_path); ClassDB::bind_method(D_METHOD("get_path_to", "node"), &Node::get_path_to); diff --git a/scene/main/node.h b/scene/main/node.h index 0d1685a2be..f685eac86a 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -282,7 +282,7 @@ public: _FORCE_INLINE_ bool is_inside_tree() const { return data.inside_tree; } - bool is_a_parent_of(const Node *p_node) const; + bool is_ancestor_of(const Node *p_node) const; bool is_greater_than(const Node *p_node) const; NodePath get_path() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 6c46db7801..4b52c4e99f 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -623,7 +623,7 @@ void SceneTree::set_quit_on_go_back(bool p_enable) { #ifdef TOOLS_ENABLED bool SceneTree::is_node_being_edited(const Node *p_node) const { - return Engine::get_singleton()->is_editor_hint() && edited_scene_root && (edited_scene_root->is_a_parent_of(p_node) || edited_scene_root == p_node); + return Engine::get_singleton()->is_editor_hint() && edited_scene_root && (edited_scene_root->is_ancestor_of(p_node) || edited_scene_root == p_node); } #endif diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index a5ceec9c8b..ea9f51aa5d 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -37,7 +37,7 @@ void Timer::_notification(int p_what) { case NOTIFICATION_READY: { if (autostart) { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) { break; } #endif diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 4c9c1c0055..cc56d6a49e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1770,7 +1770,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ } Control *drag_preview = _gui_get_drag_preview(); - if (!drag_preview || (c != drag_preview && !drag_preview->is_a_parent_of(c))) { + if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) { r_inv_xform = matrix; return c; } @@ -3023,7 +3023,7 @@ void Viewport::input(const Ref<InputEvent> &p_event, bool p_local_coords) { return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { return; } @@ -3065,7 +3065,7 @@ void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coor return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) { return; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 7027319554..cb66d5724d 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -822,9 +822,9 @@ void BaseMaterial3D::_update_shader() { code += "\tTANGENT+= vec3(1.0,0.0,0.0) * abs(NORMAL.z);\n"; code += "\tTANGENT = normalize(TANGENT);\n"; - code += "\tBINORMAL = vec3(0.0,-1.0,0.0) * abs(NORMAL.x);\n"; - code += "\tBINORMAL+= vec3(0.0,0.0,1.0) * abs(NORMAL.y);\n"; - code += "\tBINORMAL+= vec3(0.0,-1.0,0.0) * abs(NORMAL.z);\n"; + code += "\tBINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);\n"; + code += "\tBINORMAL+= vec3(0.0,0.0,-1.0) * abs(NORMAL.y);\n"; + code += "\tBINORMAL+= vec3(0.0,1.0,0.0) * abs(NORMAL.z);\n"; code += "\tBINORMAL = normalize(BINORMAL);\n"; } @@ -878,6 +878,20 @@ void BaseMaterial3D::_update_shader() { code += "\tvec2 base_uv2 = UV2;\n"; } + if (features[FEATURE_HEIGHT_MAPPING] && flags[FLAG_UV1_USE_TRIPLANAR]) { + // Display both resource name and albedo texture name. + // Materials are often built-in to scenes, so displaying the resource name alone may not be meaningful. + // On the other hand, albedo textures are almost always external to the scene. + if (textures[TEXTURE_ALBEDO].is_valid()) { + WARN_PRINT(vformat("%s (albedo %s): Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path(), textures[TEXTURE_ALBEDO]->get_path())); + } else if (!get_path().is_empty()) { + WARN_PRINT(vformat("%s: Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping.", get_path())); + } else { + // Resource wasn't saved yet. + WARN_PRINT("Height mapping is not supported on triplanar materials. Ignoring height mapping in favor of triplanar mapping."); + } + } + if (!RenderingServer::get_singleton()->is_low_end() && features[FEATURE_HEIGHT_MAPPING] && !flags[FLAG_UV1_USE_TRIPLANAR]) { //heightmap not supported with triplanar code += "\t{\n"; code += "\t\tvec3 view_dir = normalize(normalize(-VERTEX)*mat3(TANGENT*heightmap_flip.x,-BINORMAL*heightmap_flip.y,NORMAL));\n"; // binormal is negative due to mikktspace, flip 'unflips' it ;-) diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 913f1ca376..9bb2a4ddb8 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map StringName type = p_node->get_class(); Ref<Script> script = p_node->get_script(); - if (script.is_valid()) { + if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) { + // Should be called in the editor only and not at runtime, + // otherwise it can cause problems because of missing instance state support. script->update_exports(); } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index ef4a53cb0d..4f854ff229 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,7 +30,10 @@ #include "tile_set.h" +#include "core/core_string_names.h" #include "core/math/geometry_2d.h" +#include "core/templates/local_vector.h" + #include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -38,6 +41,25 @@ /////////////////////////////// TileSet ////////////////////////////////////// +const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { + "right_side", + "right_corner", + "bottom_right_side", + "bottom_right_corner", + "bottom_side", + "bottom_corner", + "bottom_left_side", + "bottom_left_corner", + "left_side", + "left_corner", + "top_left_side", + "top_left_corner", + "top_side", + "top_corner", + "top_right_side", + "top_right_corner" +}; + // --- Plugins --- Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { return tile_set_plugins_vector; @@ -51,6 +73,8 @@ void TileSet::set_tile_shape(TileSet::TileShape p_shape) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -72,6 +96,8 @@ void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { @@ -81,20 +107,14 @@ TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { void TileSet::set_tile_size(Size2i p_size) { ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); tile_size = p_size; + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } Size2i TileSet::get_tile_size() const { return tile_size; } -void TileSet::set_tile_skew(Vector2 p_skew) { - emit_changed(); - tile_skew = p_skew; -} -Vector2 TileSet::get_tile_skew() const { - return tile_skew; -} - int TileSet::get_next_source_id() const { return next_source_id; } @@ -117,7 +137,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source p_tile_set_source->set_tile_set(this); _compute_next_source_id(); - sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); emit_changed(); @@ -127,7 +147,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source void TileSet::remove_source(int p_source_id) { ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); - sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); sources[p_source_id]->set_tile_set(nullptr); sources.erase(p_source_id); @@ -240,80 +260,6 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { - // TODO: optimize this with 2D meshes when they work again. - if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { - if (p_filled && p_texture.is_valid()) { - p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color); - } else { - p_canvas_item->draw_rect(p_region, p_color, p_filled); - } - } else { - float overlap = 0.0; - switch (get_tile_shape()) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - - Vector<Vector2> uvs; - uvs.append(Vector2(0.5, 0.0)); - uvs.append(Vector2(0.0, overlap)); - uvs.append(Vector2(0.0, 1.0 - overlap)); - uvs.append(Vector2(0.5, 1.0)); - uvs.append(Vector2(1.0, 1.0 - overlap)); - uvs.append(Vector2(1.0, overlap)); - uvs.append(Vector2(0.5, 0.0)); - if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); - } - } - - Vector<Vector2> points; - for (int i = 0; i < uvs.size(); i++) { - points.append(p_region.position + uvs[i] * p_region.size); - } - - if (p_filled) { - // This does hurt performances a lot. We should use a mesh if possible instead. - p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture); - - // Should improve performances, but does not work as draw_primitive does not work with textures :/ : - /*for (int i = 0; i < 6; i += 3) { - Vector<Vector2> quad; - quad.append(points[i]); - quad.append(points[(i + 1) % points.size()]); - quad.append(points[(i + 2) % points.size()]); - quad.append(points[(i + 3) % points.size()]); - - Vector<Vector2> uv_quad; - uv_quad.append(uvs[i]); - uv_quad.append(uvs[(i + 1) % uvs.size()]); - uv_quad.append(uvs[(i + 2) % uvs.size()]); - uv_quad.append(uvs[(i + 3) % uvs.size()]); - - p_control->draw_primitive(quad, Vector<Color>(), uv_quad, p_texture); - }*/ - - } else { - // This does hurt performances a lot. We should use a mesh if possible instead. - // tile_shape_grid->draw_polyline(points, p_color); - for (int i = 0; i < points.size() - 1; i++) { - p_canvas_item->draw_line(points[i], points[i + 1], p_color); - } - } - } -} - // Physics void TileSet::set_physics_layers_count(int p_physics_layers_count) { ERR_FAIL_COND(p_physics_layers_count < 0); @@ -459,14 +405,9 @@ Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const { return terrain_sets[p_terrain_set].terrains[p_terrain_index].color; } -bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { - if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { - return false; - } - - TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); +bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || @@ -474,7 +415,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || @@ -483,7 +424,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || @@ -491,7 +432,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || @@ -501,7 +442,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } else { if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -511,7 +452,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -522,7 +463,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -532,7 +473,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -547,6 +488,15 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return false; } +bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { + if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { + return false; + } + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit); +} + // Navigation void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { ERR_FAIL_COND(p_navigation_layers_count < 0); @@ -657,9 +607,926 @@ Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { return custom_data_layers[p_layer_id].type; } +Vector<Vector2> TileSet::get_tile_shape_polygon() { + Vector<Vector2> points; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + points.append(Vector2(0.0, 0.0)); + points.append(Vector2(1.0, 0.0)); + points.append(Vector2(1.0, 1.0)); + points.append(Vector2(0.0, 1.0)); + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + + points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, overlap)); + points.append(Vector2(0.0, 1.0 - overlap)); + points.append(Vector2(0.5, 1.0)); + points.append(Vector2(1.0, 1.0 - overlap)); + points.append(Vector2(1.0, overlap)); + points.append(Vector2(0.5, 0.0)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < points.size(); i++) { + points.write[i] = Vector2(points[i].y, points[i].x); + } + } + } + for (int i = 0; i < points.size(); i++) { + points.write[i] = points[i] * tile_size - tile_size / 2; + } + return points; +} + +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { + if (tile_meshes_dirty) { + Vector<Vector2> uvs = get_tile_shape_polygon(); + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + } + + Vector<Color> colors; + colors.resize(uvs.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + + // Filled mesh. + tile_filled_mesh->clear_surfaces(); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + // Lines mesh. + tile_lines_mesh->clear_surfaces(); + a.clear(); + a.resize(Mesh::ARRAY_MAX); + // Add the first point again when drawing lines. + uvs.push_back(uvs[0]); + colors.push_back(colors[0]); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + tile_meshes_dirty = false; + } + + Transform2D xform; + xform.scale(p_region.size); + xform.set_origin(p_region.get_position()); + if (p_filled) { + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + } else { + p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color); + } +} + +Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { + ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>()); + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_square_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_square_side_terrain_bit_polygon(tile_size, p_bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } + } +} + +#define TERRAIN_ALPHA 0.6 + +void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data) { + ERR_FAIL_COND(!p_tile_data); + + if (terrain_bits_meshes_dirty) { + // Recompute the meshes. + terrain_bits_meshes.clear(); + + for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) { + TerrainMode terrain_mode = TerrainMode(terrain_mode_index); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + + if (is_valid_peering_bit_for_mode(terrain_mode, bit)) { + Vector<Vector2> polygon; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_square_side_terrain_bit_polygon(tile_size, bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } + } + + Ref<ArrayMesh> mesh; + mesh.instantiate(); + Vector<Vector2> uvs; + uvs.resize(polygon.size()); + Vector<Color> colors; + colors.resize(polygon.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = polygon; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + terrain_bits_meshes[terrain_mode][bit] = mesh; + } + } + } + terrain_bits_meshes_dirty = false; + } + + int terrain_set = p_tile_data->get_terrain_set(); + if (terrain_set < 0) { + return; + } + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + if (is_valid_peering_bit_terrain(terrain_set, bit)) { + int terrain_id = p_tile_data->get_peering_bit_terrain(bit); + if (terrain_id >= 0) { + Color color = get_terrain_color(terrain_set, terrain_id); + color.a = TERRAIN_ALPHA; + p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color); + } + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { + // Counts the number of matching terrain tiles and find the best matching icon. + struct Count { + int count = 0; + float probability = 0.0; + Ref<Texture2D> texture; + Rect2i region; + }; + Vector<Vector<Ref<Texture2D>>> output; + LocalVector<LocalVector<Count>> counts; + output.resize(get_terrain_sets_count()); + counts.resize(get_terrain_sets_count()); + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + output.write[terrain_set].resize(get_terrains_count(terrain_set)); + counts[terrain_set].resize(get_terrains_count(terrain_set)); + } + + for (int source_index = 0; source_index < get_source_count(); source_index++) { + int source_id = get_source_id(source_index); + Ref<TileSetSource> source = get_source(source_id); + + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) { + Vector2i tile_id = source->get_tile_id(tile_index); + for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) { + int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index); + + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id)); + int terrain_set = tile_data->get_terrain_set(); + if (terrain_set >= 0) { + ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>()); + + LocalVector<int> bit_counts; + bit_counts.resize(get_terrains_count(terrain_set)); + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + bit_counts[terrain] = 0; + } + for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) { + TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit); + if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { + int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); + if (terrain >= 0) { + bit_counts[terrain] += 1; + } + } + } + + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + if ((bit_counts[terrain] > counts[terrain_set][terrain].count) || (bit_counts[terrain] == counts[terrain_set][terrain].count && tile_data->get_probability() > counts[terrain_set][terrain].probability)) { + counts[terrain_set][terrain].count = bit_counts[terrain]; + counts[terrain_set][terrain].probability = tile_data->get_probability(); + counts[terrain_set][terrain].texture = atlas_source->get_texture(); + counts[terrain_set][terrain].region = atlas_source->get_tile_texture_region(tile_id); + } + } + } + } + } + } + } + + // Generate the icons. + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + Ref<Image> image; + image.instantiate(); + if (counts[terrain_set][terrain].count > 0) { + // Get the best tile. + Ref<Texture2D> texture = counts[terrain_set][terrain].texture; + Rect2 region = counts[terrain_set][terrain].region; + image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->blit_rect(texture->get_image(), region, Point2()); + image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); + } else { + image->create(1, 1, false, Image::FORMAT_RGBA8); + image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); + } + Ref<ImageTexture> icon; + icon.instantiate(); + icon->create_from_image(image); + icon->set_size_override(p_size); + + output.write[terrain_set].write[terrain] = icon; + } + } + return output; +} + void TileSet::_source_changed() { emit_changed(); - notify_property_list_changed(); +} + +Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Rect2 bit_rect; + bit_rect.size = Vector2(p_size) / 3; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit_rect.position = Vector2(1, -1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit_rect.position = Vector2(1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit_rect.position = Vector2(-1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit_rect.position = Vector2(-3, 1); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit_rect.position = Vector2(-3, -1); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit_rect.position = Vector2(-3, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit_rect.position = Vector2(-1, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit_rect.position = Vector2(1, -3); + break; + default: + break; + } + bit_rect.position *= Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + polygon.push_back(bit_rect.position); + polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y)); + polygon.push_back(bit_rect.get_end()); + polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y)); + return polygon; +} + +Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(1, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(1, 2) * unit); + polygon.push_back(Vector2(2, 1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1, 2) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1, -2) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(1, -2) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector<Vector2> polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, 0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; +} + +Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector<Vector2> point_list; + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } + + Vector<Vector2> polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + default: + break; + } + } + + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } + + return polygon; } void TileSet::reset_state() { @@ -747,12 +1614,18 @@ void TileSet::compatibility_conversion() { for (int k = 0; k < ctd->shapes.size(); k++) { CompatibilityShapeData csd = ctd->shapes[k]; if (csd.autotile_coords == coords) { - tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1); - int index = tile_data->get_collision_shapes_count(0) - 1; - tile_data->set_collision_shape_one_way(0, index, csd.one_way); - tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin); - tile_data->set_collision_shape_shape(0, index, csd.shape); - // Ignores transform for now. + Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor + if (convex_shape.is_valid()) { + Vector<Vector2> polygon = convex_shape->get_points(); + for (int point_index = 0; point_index < polygon.size(); point_index++) { + polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + } + tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); + int index = tile_data->get_collision_polygons_count(0) - 1; + tile_data->set_collision_polygon_one_way(0, index, csd.one_way); + tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_polygon_points(0, index, polygon); + } } } @@ -1086,7 +1959,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { return true; } } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { - // Create source only if it does not exist. + // Create source only if it does not exists. int source_id = components[1].to_int(); if (!has_source(source_id)) { @@ -1287,14 +2160,11 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); - ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew); - ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew"); // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); @@ -1398,10 +2268,13 @@ void TileSet::_bind_methods() { } TileSet::TileSet() { - // Instanciatie and list all plugins. + // Instantiate the tile meshes. + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); + + // Instanciate and list all plugins. tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasTerrain)); tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); } @@ -2237,16 +3110,14 @@ void TileSetScenesCollectionSource::_bind_methods() { void TileData::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; - if (tile_set) { - occluders.resize(tile_set->get_occlusion_layers_count()); - physics.resize(tile_set->get_physics_layers_count()); - navigation.resize(tile_set->get_navigation_layers_count()); - custom_data.resize(tile_set->get_custom_data_layers_count()); - } - notify_property_list_changed(); + notify_tile_data_properties_should_change(); } void TileData::notify_tile_data_properties_should_change() { + if (!tile_set) { + return; + } + occluders.resize(tile_set->get_occlusion_layers_count()); physics.resize(tile_set->get_physics_layers_count()); for (int bit_index = 0; bit_index < 16; bit_index++) { @@ -2373,76 +3244,112 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_shapes_count(int p_layer_id) const { +int TileData::get_collision_polygons_count(int p_layer_id) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].shapes.size(); + return physics[p_layer_id].polygons.size(); } -void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) { +void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_COND(p_shapes_count < 0); - physics.write[p_layer_id].shapes.resize(p_shapes_count); + ERR_FAIL_COND(p_polygons_count < 0); + physics.write[p_layer_id].polygons.resize(p_polygons_count); notify_property_list_changed(); emit_signal("changed"); } -void TileData::add_collision_shape(int p_layer_id) { +void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData()); + physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); emit_signal("changed"); } -void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) { +void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.remove(p_shape_index); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.remove(p_polygon_index); emit_signal("changed"); } -void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape) { +void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points."); + + if (p_polygon.is_empty()) { + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear(); + } else { + // Decompose into convex shapes. + Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon); + ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes."); + + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size()); + for (int i = 0; i < decomp.size(); i++) { + Ref<ConvexPolygonShape2D> shape; + shape.instantiate(); + shape->set_points(decomp[i]); + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape; + } + } + physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon; emit_signal("changed"); } -Ref<Shape2D> TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<Shape2D>()); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref<Shape2D>()); - return physics[p_layer_id].shapes[p_shape_index].shape; +Vector<Vector2> TileData::get_collision_polygon_points(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector<Vector2>()); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Vector<Vector2>()); + return physics[p_layer_id].polygons[p_polygon_index].polygon; } -void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) { +void TileData::set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way = p_one_way; emit_signal("changed"); } -bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const { +bool TileData::is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false); - return physics[p_layer_id].shapes[p_shape_index].one_way; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), false); + return physics[p_layer_id].polygons[p_polygon_index].one_way; } -void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) { +void TileData::set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin) { ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin; + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way_margin = p_one_way_margin; emit_signal("changed"); } -float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const { +float TileData::get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const { ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0); - return physics[p_layer_id].shapes[p_shape_index].one_way_margin; + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0.0); + return physics[p_layer_id].polygons[p_polygon_index].one_way_margin; +} + +int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0); + return physics[p_layer_id].polygons[p_polygon_index].shapes.size(); +} + +Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>()); + ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>()); + return physics[p_layer_id].polygons[shape_index].shapes[shape_index]; } // Terrain void TileData::set_terrain_set(int p_terrain_set) { ERR_FAIL_COND(p_terrain_set < -1); + if (p_terrain_set == terrain_set) { + return; + } if (tile_set) { ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; + } } terrain_set = p_terrain_set; notify_property_list_changed(); @@ -2454,7 +3361,7 @@ int TileData::get_terrain_set() const { } void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { - ERR_FAIL_INDEX(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX); + ERR_FAIL_COND(terrain_set < 0); ERR_FAIL_COND(p_terrain_index < -1); if (tile_set) { ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); @@ -2465,7 +3372,7 @@ void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int } int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { - ERR_FAIL_INDEX_V(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX, -1); + ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1); return terrain_peering_bits[p_peering_bit]; } @@ -2489,7 +3396,7 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const { // Misc void TileData::set_probability(float p_probability) { - ERR_FAIL_COND(p_probability <= 0.0); + ERR_FAIL_COND(p_probability < 0.0); probability = p_probability; emit_signal("changed"); } @@ -2550,7 +3457,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "shapes_count") { + if (components.size() == 2 && components[1] == "polygons_count") { if (p_value.get_type() != Variant::INT) { return false; } @@ -2562,13 +3469,13 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_shapes_count(layer_index, p_value); + set_collision_polygons_count(layer_index, p_value); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); - if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") { + if (components[2] == "points" || components[2] == "one_way" || components[2] == "one_way_margin") { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -2577,19 +3484,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } - if (shape_index >= physics[layer_index].shapes.size()) { - physics.write[layer_index].shapes.resize(shape_index + 1); + if (polygon_index >= physics[layer_index].polygons.size()) { + physics.write[layer_index].polygons.resize(polygon_index + 1); } } - if (components[2] == "shape") { - Ref<Shape2D> shape = p_value; - set_collision_shape_shape(layer_index, shape_index, shape); + if (components[2] == "points") { + Vector<Vector2> polygon = p_value; + set_collision_polygon_points(layer_index, polygon_index, polygon); return true; } else if (components[2] == "one_way") { - set_collision_shape_one_way(layer_index, shape_index, p_value); + set_collision_polygon_one_way(layer_index, polygon_index, p_value); return true; } else if (components[2] == "one_way_margin") { - set_collision_shape_one_way_margin(layer_index, shape_index, p_value); + set_collision_polygon_one_way_margin(layer_index, polygon_index, p_value); return true; } } @@ -2615,42 +3522,14 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value); - } else if (components[1] == "right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value); - } else if (components[1] == "bottom_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value); - } else if (components[1] == "bottom_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value); - } else if (components[1] == "bottom_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value); - } else if (components[1] == "bottom_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value); - } else if (components[1] == "left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value); - } else if (components[1] == "left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value); - } else if (components[1] == "top_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value); - } else if (components[1] == "top_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value); - } else if (components[1] == "top_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value); - } else if (components[1] == "top_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value); - } else if (components[1] == "top_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value); - } else if (components[1] == "top_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value); - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + set_peering_bit_terrain(bit, p_value); + return true; + } } - return true; + return false; } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { // Custom data layers. int layer_index = components[0].trim_prefix("custom_data_").to_int(); @@ -2693,64 +3572,35 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "shapes_count") { - r_ret = get_collision_shapes_count(layer_index); + if (components.size() == 2 && components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); - if (shape_index >= physics[layer_index].shapes.size()) { + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (polygon_index >= physics[layer_index].polygons.size()) { return false; } - if (components[2] == "shape") { - r_ret = get_collision_shape_shape(layer_index, shape_index); + if (components[2] == "points") { + r_ret = get_collision_polygon_points(layer_index, polygon_index); return true; } else if (components[2] == "one_way") { - r_ret = is_collision_shape_one_way(layer_index, shape_index); + r_ret = is_collision_polygon_one_way(layer_index, polygon_index); return true; } else if (components[2] == "one_way_margin") { - r_ret = get_collision_shape_one_way_margin(layer_index, shape_index); + r_ret = get_collision_polygon_one_way_margin(layer_index, polygon_index); return true; } } } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { // Terrains. - if (components[1] == "right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE]; - } else if (components[1] == "right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER]; - } else if (components[1] == "bottom_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE]; - } else if (components[1] == "bottom_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER]; - } else if (components[1] == "bottom_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE]; - } else if (components[1] == "bottom_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER]; - } else if (components[1] == "bottom_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE]; - } else if (components[1] == "bottom_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER]; - } else if (components[1] == "left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE]; - } else if (components[1] == "left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER]; - } else if (components[1] == "top_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE]; - } else if (components[1] == "top_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER]; - } else if (components[1] == "top_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE]; - } else if (components[1] == "top_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER]; - } else if (components[1] == "top_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE]; - } else if (components[1] == "top_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER]; - } else { - return false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + r_ret = terrain_peering_bits[i]; + return true; + } } - return true; + return false; } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); @@ -2795,26 +3645,26 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - for (int j = 0; j < physics[i].shapes.size(); j++) { - // physics_layer_%d/shapes_count - property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT); - if (!physics[i].shapes[j].shape.is_valid()) { + for (int j = 0; j < physics[i].polygons.size(); j++) { + // physics_layer_%d/points + property_info = PropertyInfo(Variant::ARRAY, vformat("physics_layer_%d/polygon_%d/points", i, j), PROPERTY_HINT_ARRAY_TYPE, "Vector2", PROPERTY_USAGE_DEFAULT); + if (physics[i].polygons[j].polygon.is_empty()) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way - property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j)); - if (physics[i].shapes[j].one_way == false) { + // physics_layer_%d/polygon_%d/one_way + property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/polygon_%d/one_way", i, j)); + if (physics[i].polygons[j].one_way == false) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); - // physics_layer_%d/shape_%d/one_way_margin - property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j)); - if (physics[i].shapes[j].one_way_margin == 1.0) { + // physics_layer_%d/polygon_%d/one_way_margin + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/polygon_%d/one_way_margin", i, j)); + if (physics[i].polygons[j].one_way_margin == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } p_list->push_back(property_info); @@ -2824,117 +3674,15 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Terrain data if (terrain_set >= 0) { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (is_valid_peering_bit_terrain(bit)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); + if (get_peering_bit_terrain(bit) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); } - p_list->push_back(property_info); } } @@ -2986,16 +3734,16 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count); - ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count); - ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape); - ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape); - ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); + ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); + ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); + ClassDB::bind_method(D_METHOD("get_collision_polygon_points", "layer_id", "polygon_index"), &TileData::get_collision_polygon_points); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way", "layer_id", "polygon_index", "one_way"), &TileData::set_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("is_collision_polygon_one_way", "layer_id", "polygon_index"), &TileData::is_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way_margin", "layer_id", "polygon_index", "one_way_margin"), &TileData::set_collision_polygon_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygon_one_way_margin", "layer_id", "polygon_index"), &TileData::get_collision_polygon_one_way_margin); // Terrain ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); @@ -3034,805 +3782,6 @@ void TileData::_bind_methods() { ADD_SIGNAL(MethodInfo("changed")); } - -/////////////////////////////// TileSetPluginAtlasTerrain ////////////////////////////////////// - -// --- PLUGINS --- -void TileSetPluginAtlasTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - Rect2 bit_rect; - bit_rect.size = Vector2(p_size) / 3; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit_rect.position = Vector2(1, -1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit_rect.position = Vector2(1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit_rect.position = Vector2(-1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit_rect.position = Vector2(-3, 1); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit_rect.position = Vector2(-3, -1); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit_rect.position = Vector2(-3, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit_rect.position = Vector2(-1, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit_rect.position = Vector2(1, -3); - break; - default: - break; - } - bit_rect.position *= Vector2(p_size) / 6.0; - p_canvas_item->draw_rect(bit_rect, p_color); -} - -void TileSetPluginAtlasTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(1, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(1, 2) * unit); - polygon.push_back(Vector2(2, 1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1, 2) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1, -2) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(1, -2) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); - - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, 0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -void TileSetPluginAtlasTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); - - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } - - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - default: - break; - } - } - - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } - - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } -} - -#define TERRAIN_ALPHA 0.8 - -#define DRAW_TERRAIN_BIT(f, bit) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit)); \ - } \ - } - -#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \ - } \ - } - -void TileSetPluginAtlasTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) { - ERR_FAIL_COND(!p_tile_set); - ERR_FAIL_COND(!p_tile_data); - - int terrain_set = p_tile_data->get_terrain_set(); - if (terrain_set < 0) { - return; - } - TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set); - - TileSet::TileShape shape = p_tile_set->get_tile_shape(); - Vector2i size = p_tile_set->get_tile_size(); - - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } - } else { - TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis(); - float overlap = 0.0; - switch (p_tile_set->get_tile_shape()) { - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } - } - } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); -} - /////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { @@ -4218,15 +4167,17 @@ void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self for (int body_index = 0; body_index < q.bodies.size(); body_index++) { // Add the shapes again. - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index); - float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index); - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { Transform2D xform = Transform2D(); xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); @@ -4343,11 +4294,13 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - // Draw the debug shape. - Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { - shape->draw(p_quadrant->debug_canvas_item, debug_collision_color); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector<Color> color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); } } } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 6cf4198f30..dbf6dbabe6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -33,19 +33,18 @@ #include "core/io/resource.h" #include "core/object/object.h" +#include "core/templates/local_vector.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" #ifndef DISABLE_DEPRECATED -#include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/resources/shader.h" -#include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" #endif @@ -60,7 +59,6 @@ class TileSetPlugin; class TileSetPluginAtlasRendering; class TileSetPluginAtlasPhysics; class TileSetPluginAtlasNavigation; -class TileSetPluginAtlasTerrain; class TileSet : public Resource { GDCLASS(TileSet, Resource); @@ -138,6 +136,8 @@ public: CELL_NEIGHBOR_MAX, }; + static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[]; + enum TerrainMode { TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0, TERRAIN_MODE_MATCH_CORNERS, @@ -194,6 +194,10 @@ private: }; Vector<OcclusionLayer> occlusion_layers; + Ref<ArrayMesh> tile_lines_mesh; + Ref<ArrayMesh> tile_filled_mesh; + bool tile_meshes_dirty = true; + // Physics struct PhysicsLayer { uint32_t collision_layer = 1; @@ -213,6 +217,9 @@ private: }; Vector<TerrainSet> terrain_sets; + Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; + bool terrain_bits_meshes_dirty = true; + // Navigation struct Navigationlayer { uint32_t layers = 1; @@ -239,6 +246,19 @@ private: void _compute_next_source_id(); void _source_changed(); + // Helpers + Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector<Point2> _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector<Point2> _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector<Point2> _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + protected: static void _bind_methods(); @@ -257,8 +277,6 @@ public: TileOffsetAxis get_tile_offset_axis() const; void set_tile_size(Size2i p_size); Size2i get_tile_size() const; - void set_tile_skew(Vector2 p_skew); - Vector2 get_tile_skew() const; // -- Sources management -- int get_next_source_id() const; @@ -305,6 +323,7 @@ public: String get_terrain_name(int p_terrain_set, int p_terrain_index) const; void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); Color get_terrain_color(int p_terrain_set, int p_terrain_index) const; + bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const; bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation @@ -323,8 +342,14 @@ public: Variant::Type get_custom_data_type(int p_layer_id) const; // Helpers + Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); + Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit); + void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data); + Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size); + + // Resource management virtual void reset_state() override; TileSet(); @@ -509,13 +534,14 @@ private: // Physics struct PhysicsLayerTileData { - struct ShapeTileData { - Ref<Shape2D> shape = Ref<Shape2D>(); + struct PolygonShapeTileData { + LocalVector<Vector2> polygon; + LocalVector<Ref<ConvexPolygonShape2D>> shapes; bool one_way = false; float one_way_margin = 1.0; }; - Vector<ShapeTileData> shapes; + Vector<PolygonShapeTileData> polygons; }; Vector<PhysicsLayerTileData> physics; // TODO add support for areas. @@ -570,16 +596,18 @@ public: Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const; // Physics - int get_collision_shapes_count(int p_layer_id) const; - void set_collision_shapes_count(int p_layer_id, int p_shapes_count); - void add_collision_shape(int p_layer_id); - void remove_collision_shape(int p_layer_id, int p_shape_index); - void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape); - Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way); - bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin); - float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const; + int get_collision_polygons_count(int p_layer_id) const; + void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + void add_collision_polygon(int p_layer_id); + void remove_collision_polygon(int p_layer_id, int p_polygon_index); + void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon); + Vector<Vector2> get_collision_polygon_points(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way); + bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin); + float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const; + int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const; + Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const; // Terrain void set_terrain_set(int p_terrain_id); @@ -637,26 +665,6 @@ public: static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); }; -class TileSetPluginAtlasTerrain : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasTerrain, TileSetPlugin); - -private: - static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - -public: - static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data); -}; - class TileSetPluginAtlasPhysics : public TileSetPlugin { GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 774c1a5c33..54bc7382db 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2533,6 +2533,14 @@ Vector<StringName> VisualShaderNodeInput::get_editable_properties() const { return props; } +void VisualShaderNodeInput::set_shader_type(VisualShader::Type p_shader_type) { + shader_type = p_shader_type; +} + +void VisualShaderNodeInput::set_shader_mode(Shader::Mode p_shader_mode) { + shader_mode = p_shader_mode; +} + void VisualShaderNodeInput::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name); ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 53b165fe0f..454012b7ed 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -350,6 +350,10 @@ class VisualShaderNodeInput : public VisualShaderNode { String input_name = "[None]"; +public: + void set_shader_type(VisualShader::Type p_shader_type); + void set_shader_mode(Shader::Mode p_shader_mode); + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const override; diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp index 4fd5520e56..4290e0d574 100644 --- a/servers/rendering/renderer_rd/effects_rd.cpp +++ b/servers/rendering/renderer_rd/effects_rd.cpp @@ -37,21 +37,6 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "thirdparty/misc/cubemap_coeffs.h" -static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) { - p_array[0] = p_basis.elements[0][0]; - p_array[1] = p_basis.elements[1][0]; - p_array[2] = p_basis.elements[2][0]; - p_array[3] = 0; - p_array[4] = p_basis.elements[0][1]; - p_array[5] = p_basis.elements[1][1]; - p_array[6] = p_basis.elements[2][1]; - p_array[7] = 0; - p_array[8] = p_basis.elements[0][2]; - p_array[9] = p_basis.elements[1][2]; - p_array[10] = p_basis.elements[2][2]; - p_array[11] = 0; -} - static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -1371,45 +1356,6 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, RD::get_singleton()->compute_list_end(); } -void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { - SkyPushConstant sky_push_constant; - - memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); - - for (uint32_t v = 0; v < p_view_count; v++) { - // We only need key components of our projection matrix - sky_push_constant.projections[v][0] = p_projections[v].matrix[2][0]; - sky_push_constant.projections[v][1] = p_projections[v].matrix[0][0]; - sky_push_constant.projections[v][2] = p_projections[v].matrix[2][1]; - sky_push_constant.projections[v][3] = p_projections[v].matrix[1][1]; - } - sky_push_constant.position[0] = p_position.x; - sky_push_constant.position[1] = p_position.y; - sky_push_constant.position[2] = p_position.z; - sky_push_constant.multiplier = p_multiplier; - sky_push_constant.time = p_time; - store_transform_3x3(p_orientation, sky_push_constant.orientation); - - RenderingDevice::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_fb); - - RD::DrawListID draw_list = p_list; - - RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format)); - - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_samplers, 0); - if (p_uniform_set.is_valid()) { //material may not have uniform set - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); - } - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_fog, 3); - - RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); - - RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant)); - - RD::get_singleton()->draw_list_draw(draw_list, true); -} - void EffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) { ResolvePushConstant push_constant; push_constant.screen_size[0] = p_screen_size.x; diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h index 8b31ffbbd0..b4ddd400a8 100644 --- a/servers/rendering/renderer_rd/effects_rd.h +++ b/servers/rendering/renderer_rd/effects_rd.h @@ -456,15 +456,6 @@ class EffectsRD { } filter; - struct SkyPushConstant { - float orientation[12]; // 48 - 48 - float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 64, if we ever need more then 3 we should consider adding this to a set. - float position[3]; // 12 - 92 - float multiplier; // 4 - 96 - float time; // 4 - 100 - float pad[3]; // 12 - 112 - }; - enum SpecularMergeMode { SPECULAR_MERGE_ADD, SPECULAR_MERGE_SSR, @@ -749,7 +740,6 @@ public: void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); - void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera); void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection); diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index 2082ac9150..956609b77e 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -323,6 +323,63 @@ RendererSceneSkyRD::SkyMaterialData::~SkyMaterialData() { } //////////////////////////////////////////////////////////////////////////////// +// Render sky + +static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) { + p_array[0] = p_basis.elements[0][0]; + p_array[1] = p_basis.elements[1][0]; + p_array[2] = p_basis.elements[2][0]; + p_array[3] = 0; + p_array[4] = p_basis.elements[0][1]; + p_array[5] = p_basis.elements[1][1]; + p_array[6] = p_basis.elements[2][1]; + p_array[7] = 0; + p_array[8] = p_basis.elements[0][2]; + p_array[9] = p_basis.elements[1][2]; + p_array[10] = p_basis.elements[2][2]; + p_array[11] = 0; +} + +void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { + SkyPushConstant sky_push_constant; + + memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); + + for (uint32_t v = 0; v < p_view_count; v++) { + // We only need key components of our projection matrix + sky_push_constant.projections[v][0] = p_projections[v].matrix[2][0]; + sky_push_constant.projections[v][1] = p_projections[v].matrix[0][0]; + sky_push_constant.projections[v][2] = p_projections[v].matrix[2][1]; + sky_push_constant.projections[v][3] = p_projections[v].matrix[1][1]; + } + sky_push_constant.position[0] = p_position.x; + sky_push_constant.position[1] = p_position.y; + sky_push_constant.position[2] = p_position.z; + sky_push_constant.multiplier = p_multiplier; + sky_push_constant.time = p_time; + store_transform_3x3(p_orientation, sky_push_constant.orientation); + + RenderingDevice::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_fb); + + RD::DrawListID draw_list = p_list; + + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, p_pipeline->get_render_pipeline(RD::INVALID_ID, fb_format)); + + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0); + if (p_uniform_set.is_valid()) { //material may not have uniform set + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); + } + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, 3); + + RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); + + RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant)); + + RD::get_singleton()->draw_list_draw(draw_list, true); +} + +//////////////////////////////////////////////////////////////////////////////// // ReflectionData void RendererSceneSkyRD::ReflectionData::clear_reflection_data() { @@ -892,6 +949,41 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) { sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES); } + + { //create index array for copy shaders + Vector<uint8_t> pv; + pv.resize(6 * 4); + { + uint8_t *w = pv.ptrw(); + int *p32 = (int *)w; + p32[0] = 0; + p32[1] = 1; + p32[2] = 2; + p32[3] = 0; + p32[4] = 2; + p32[5] = 3; + } + index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv); + index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6); + } +} + +RendererSceneSkyRD::~RendererSceneSkyRD() { + // TODO cleanup anything created in init... + + if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.uniform_set)) { + RD::get_singleton()->free(sky_scene_state.uniform_set); + } + + if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.default_fog_uniform_set)) { + RD::get_singleton()->free(sky_scene_state.default_fog_uniform_set); + } + + if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_only_texture_uniform_set)) { + RD::get_singleton()->free(sky_scene_state.fog_only_texture_uniform_set); + } + + RD::get_singleton()->free(index_buffer); //array gets freed as dependency } void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { @@ -1156,7 +1248,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } } @@ -1174,7 +1266,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } } @@ -1188,7 +1280,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } @@ -1305,7 +1397,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } @@ -1318,7 +1410,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } @@ -1332,7 +1424,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); - storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); + _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin); RD::get_singleton()->draw_list_end(); } diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 200902bff2..1292622fca 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -43,9 +43,6 @@ class RendererSceneRenderRD; class RendererSceneSkyRD { -private: - RendererStorageRD *storage; - public: enum SkySet { SKY_SET_UNIFORMS, @@ -55,6 +52,22 @@ public: SKY_SET_MAX }; + // Skys need less info from Directional Lights than the normal shaders + struct SkyDirectionalLightData { + float direction[3]; + float energy; + float color[3]; + float size; + uint32_t enabled; + uint32_t pad[3]; + }; + +private: + RendererStorageRD *storage; + + RID index_buffer; + RID index_array; + enum SkyTextureSetVersion { SKY_TEXTURE_SET_BACKGROUND, SKY_TEXTURE_SET_HALF_RES, @@ -80,16 +93,53 @@ public: SKY_VERSION_MAX }; - // Skys need less info from Directional Lights than the normal shaders - struct SkyDirectionalLightData { - float direction[3]; - float energy; - float color[3]; - float size; - uint32_t enabled; - uint32_t pad[3]; + struct SkyPushConstant { + float orientation[12]; // 48 - 48 + float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 80 + float position[3]; // 12 - 92 + float multiplier; // 4 - 96 + float time; // 4 - 100 + float pad[3]; // 12 - 112 // Using pad to align on 16 bytes + // 128 is the max size of a push constant. We can replace "pad" but we can't add any more. + }; + + struct SkyShaderData : public RendererStorageRD::ShaderData { + bool valid; + RID version; + + PipelineCacheRD pipelines[SKY_VERSION_MAX]; + Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String path; + String code; + Map<StringName, RID> default_texture_params; + + bool uses_time; + bool uses_position; + bool uses_half_res; + bool uses_quarter_res; + bool uses_light; + + virtual void set_code(const String &p_Code); + virtual void set_default_texture_param(const StringName &p_name, RID p_texture); + virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual void get_instance_param_list(List<RendererStorage::InstanceShaderParam> *p_param_list) const; + virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + SkyShaderData(); + virtual ~SkyShaderData(); }; + void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); + +public: struct SkySceneState { struct UBO { uint32_t volumetric_fog_enabled; @@ -160,40 +210,6 @@ public: void update_reflection_mipmaps(RendererStorageRD *p_storage, int p_start, int p_end); }; - struct SkyShaderData : public RendererStorageRD::ShaderData { - bool valid; - RID version; - - PipelineCacheRD pipelines[SKY_VERSION_MAX]; - Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; - Vector<ShaderCompilerRD::GeneratedCode::Texture> texture_uniforms; - - Vector<uint32_t> ubo_offsets; - uint32_t ubo_size; - - String path; - String code; - Map<StringName, RID> default_texture_params; - - bool uses_time; - bool uses_position; - bool uses_half_res; - bool uses_quarter_res; - bool uses_light; - - virtual void set_code(const String &p_Code); - virtual void set_default_texture_param(const StringName &p_name, RID p_texture); - virtual void get_param_list(List<PropertyInfo> *p_param_list) const; - virtual void get_instance_param_list(List<RendererStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_param_texture(const StringName &p_param) const; - virtual bool is_animated() const; - virtual bool casts_shadows() const; - virtual Variant get_default_parameter(const StringName &p_parameter) const; - virtual RS::ShaderNativeSourceCode get_native_source_code() const; - SkyShaderData(); - virtual ~SkyShaderData(); - }; - /* Sky shader */ struct SkyShader { @@ -270,8 +286,8 @@ public: static RendererStorageRD::MaterialData *_create_sky_material_funcs(RendererStorageRD::ShaderData *p_shader); RendererSceneSkyRD(); - void init(RendererStorageRD *p_storage); + ~RendererSceneSkyRD(); void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); |