summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/TileMap.xml94
-rw-r--r--doc/classes/TileSet.xml2
-rw-r--r--editor/icons/TileMapHighlightSelected.svg1
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp5
-rw-r--r--editor/plugins/tiles/tile_data_editors.h2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp689
-rw-r--r--editor/plugins/tiles/tile_map_editor.h35
-rw-r--r--editor/plugins/tiles/tile_proxies_manager_dialog.h2
-rw-r--r--scene/2d/tile_map.cpp1618
-rw-r--r--scene/2d/tile_map.h127
-rw-r--r--scene/gui/tree.h4
-rw-r--r--scene/resources/tile_set.cpp885
-rw-r--r--scene/resources/tile_set.h74
13 files changed, 2098 insertions, 1440 deletions
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 957619ad81..f3c64c3c7d 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -31,22 +31,43 @@
</method>
<method name="get_cell_alternative_tile" qualifiers="const">
<return type="int" />
- <argument index="0" name="coords" type="Vector2i" />
- <argument index="1" name="use_proxies" type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <argument index="2" name="use_proxies" type="bool" />
<description>
</description>
</method>
<method name="get_cell_atlas_coords" qualifiers="const">
<return type="Vector2i" />
- <argument index="0" name="coords" type="Vector2i" />
- <argument index="1" name="use_proxies" type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <argument index="2" name="use_proxies" type="bool" />
<description>
</description>
</method>
<method name="get_cell_source_id" qualifiers="const">
<return type="int" />
- <argument index="0" name="coords" type="Vector2i" />
- <argument index="1" name="use_proxies" type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <argument index="2" name="use_proxies" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="get_layer_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="layer" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_layer_y_sort_origin" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="layer" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_layer_z_indexd" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="layer" type="int" />
<description>
</description>
</method>
@@ -65,6 +86,7 @@
</method>
<method name="get_used_cells" qualifiers="const">
<return type="Vector2i[]" />
+ <argument index="0" name="layer" type="int" />
<description>
Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]).
</description>
@@ -75,6 +97,18 @@
Returns a rectangle enclosing the used (non-empty) tiles of the map.
</description>
</method>
+ <method name="is_layer_enabled" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="is_layer_y_sort_enabled" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <description>
+ </description>
+ </method>
<method name="map_to_world" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="map_position" type="Vector2i" />
@@ -84,18 +118,48 @@
</method>
<method name="set_cell">
<return type="void" />
- <argument index="0" name="coords" type="Vector2i" />
- <argument index="1" name="source_id" type="int" default="-1" />
- <argument index="2" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" />
- <argument index="3" name="alternative_tile" type="int" default="-1" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <argument index="2" name="source_id" type="int" default="-1" />
+ <argument index="3" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" />
+ <argument index="4" name="alternative_tile" type="int" default="-1" />
<description>
Sets the tile index for the cell given by a Vector2i.
</description>
</method>
- <method name="update_dirty_quadrants">
+ <method name="set_layer_enabled">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="set_layer_name">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="name" type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="set_layer_y_sort_enabled">
<return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="y_sort_enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="set_layer_y_sort_origin">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="y_sort_origin" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="set_layer_z_index">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="z_index" type="int" />
<description>
- Updates the tile map's quadrants, allowing things such as navigation and collision shapes to be immediately used if modified.
</description>
</method>
<method name="world_to_map" qualifiers="const">
@@ -110,9 +174,11 @@
<member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size" default="16">
The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size.
</member>
- <member name="show_collision" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0">
+ <member name="collision_visibility_mode" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0">
+ </member>
+ <member name="layers_count" type="int" setter="set_layers_count" getter="get_layers_count" default="1">
</member>
- <member name="show_navigation" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0">
+ <member name="navigation_visibility_mode" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0">
</member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet].
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 27e31f25a4..439c6e3830 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -329,8 +329,6 @@
</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">
- </member>
</members>
<constants>
<constant name="TILE_SHAPE_SQUARE" value="0" enum="TileShape">
diff --git a/editor/icons/TileMapHighlightSelected.svg b/editor/icons/TileMapHighlightSelected.svg
new file mode 100644
index 0000000000..de8a291b8e
--- /dev/null
+++ b/editor/icons/TileMapHighlightSelected.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m4.5 5.2011663h10.5v2.005h-10.5z" stroke-width="1.14115"/><g stroke-width=".862635"><path d="m2 4.2340105v-1h1l1 2z"/><path d="m2 8.2340105v1h1l1-2z"/><path d="m1.3786796 5.5269037-.70710677.7071068.70710677.7071068 2.1213204-.7071068z"/></g><path d="m4.5 1.7061663h10.5v2.005h-10.5z" stroke-width="1.14117"/><g stroke-width="1.14116"><path d="m4.5 8.7011663h10.5v2.0049997h-10.5z"/><path d="m4.5 12.206166h10.5v2.005h-10.5z"/></g></g></svg>
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 84e40e2ffa..13f04cb804 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -32,6 +32,7 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
+#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
@@ -259,7 +260,7 @@ void TileAtlasView::_draw_base_tiles() {
Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
// Draw the tile.
- TileSetPluginAtlasRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
+ TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
}
}
}
@@ -375,7 +376,7 @@ void TileAtlasView::_draw_alternatives() {
}
// Draw the tile.
- TileSetPluginAtlasRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
+ TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
current_pos.x += transposed ? texture_region.size.y : texture_region.size.x;
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index 781f26cc02..99998dc779 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -36,10 +36,10 @@
#include "editor/editor_node.h"
#include "editor/editor_properties.h"
+#include "scene/2d/tile_map.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 VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index e70aed8ed6..77084f551a 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -377,6 +377,11 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
return false;
}
+ if (tile_map_layer < 0) {
+ return false;
+ }
+ ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), false);
+
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
return false;
@@ -391,7 +396,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
coords_array.push_back(E->get());
}
- tile_map_clipboard = tile_map->get_pattern(coords_array);
+ tile_map_clipboard = tile_map->get_pattern(tile_map_layer, coords_array);
}
if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) {
@@ -399,8 +404,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!tile_map_selection.is_empty()) {
undo_redo->create_action(TTR("Delete tiles"));
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get()));
}
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
tile_map_selection.clear();
@@ -430,8 +435,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (!tile_map_selection.is_empty()) {
undo_redo->create_action(TTR("Delete tiles"));
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get()));
}
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
tile_map_selection.clear();
@@ -456,9 +461,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
Vector2i coords = E->key();
if (!drag_modified.has(coords)) {
- drag_modified.insert(coords, tile_map->get_cell(coords));
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
} break;
case DRAG_TYPE_BUCKET: {
@@ -472,9 +477,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
Vector2i coords = E->key();
if (!drag_modified.has(coords)) {
- drag_modified.insert(coords, tile_map->get_cell(coords));
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
}
}
@@ -497,7 +502,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
// Pressed
- if (tool_buttons_group->get_pressed_button() == select_tool_button) {
+ if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
+ // Do nothing.
+ } else if (tool_buttons_group->get_pressed_button() == select_tool_button) {
drag_start_mouse_pos = mpos;
if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
// Move the selection
@@ -505,8 +512,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
drag_modified.clear();
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
Vector2i coords = E->get();
- drag_modified.insert(coords, tile_map->get_cell(coords));
- tile_map->set_cell(coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
+ tile_map->set_cell(tile_map_layer, coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
} else {
// Select tiles
@@ -530,9 +537,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
Vector2i coords = E->key();
if (!drag_modified.has(coords)) {
- drag_modified.insert(coords, tile_map->get_cell(coords));
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
} else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
drag_type = DRAG_TYPE_LINE;
@@ -556,9 +563,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
Vector2i coords = E->key();
if (!drag_modified.has(coords)) {
- drag_modified.insert(coords, tile_map->get_cell(coords));
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
}
}
@@ -587,6 +594,11 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
return;
}
+ if (tile_map_layer < 0) {
+ return;
+ }
+ ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
+
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
return;
@@ -623,7 +635,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) {
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size));
tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false);
}
@@ -637,7 +649,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) {
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
to_draw.insert(coords);
}
}
@@ -914,6 +926,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
TileMapPattern erase_pattern;
erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ Map<Vector2i, TileMapCell> err_output;
+ ERR_FAIL_COND_V(pattern->is_empty(), err_output);
// Compute the offset to align things to the bottom or right.
bool aligned_right = p_end_cell.x < p_start_cell.x;
@@ -956,6 +970,12 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
return Map<Vector2i, TileMapCell>();
}
+ if (tile_map_layer < 0) {
+ return Map<Vector2i, TileMapCell>();
+ }
+ Map<Vector2i, TileMapCell> output;
+ ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), output);
+
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
return Map<Vector2i, TileMapCell>();
@@ -966,9 +986,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
- Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
- TileMapCell source = tile_map->get_cell(p_coords);
+ TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords);
// If we are filling empty tiles, compute the tilemap boundaries.
Rect2i boundaries;
@@ -985,9 +1004,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
Vector2i coords = to_check.back()->get();
to_check.pop_back();
if (!already_checked.has(coords)) {
- if (source.source_id == tile_map->get_cell_source_id(coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(coords) &&
+ if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
(source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
@@ -1027,13 +1046,13 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
}
}
} else {
- to_check = tile_map->get_used_cells();
+ to_check = tile_map->get_used_cells(tile_map_layer);
}
for (int i = 0; i < to_check.size(); i++) {
Vector2i coords = to_check[i];
- if (source.source_id == tile_map->get_cell_source_id(coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(coords) &&
+ if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
(source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
@@ -1066,6 +1085,11 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
return;
}
+ if (tile_map_layer < 0) {
+ return;
+ }
+ ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
+
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
return;
@@ -1091,7 +1115,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
tile_map_selection.erase(coords);
}
} else {
- if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) {
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
tile_map_selection.insert(coords);
}
}
@@ -1123,7 +1147,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords));
+ cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
}
Map<Vector2i, TileMapCell> cells_do;
@@ -1138,10 +1162,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
undo_redo->create_action(TTR("Move tiles"));
// Move the tiles.
for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
// Update the selection.
@@ -1162,12 +1186,12 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) {
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
coords_array.push_back(coords);
}
}
}
- selection_pattern = tile_map->get_pattern(coords_array);
+ selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
if (!selection_pattern->is_empty()) {
_update_tileset_selection_from_selection_pattern();
} else {
@@ -1178,8 +1202,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
case DRAG_TYPE_PAINT: {
undo_redo->create_action(TTR("Paint tiles"));
for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
undo_redo->commit_action(false);
} break;
@@ -1190,8 +1214,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
}
undo_redo->commit_action();
} break;
@@ -1202,16 +1226,16 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
}
undo_redo->commit_action();
} break;
case DRAG_TYPE_BUCKET: {
undo_redo->create_action(TTR("Paint tiles"));
for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
undo_redo->commit_action(false);
} break;
@@ -1221,8 +1245,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
TypedArray<Vector2i> used_cells = tile_map_clipboard->get_used_cells();
for (int i = 0; i < used_cells.size(); i++) {
Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(mpos - mouse_offset), used_cells[i], tile_map_clipboard);
- undo_redo->add_do_method(tile_map, "set_cell", coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i]));
- undo_redo->add_undo_method(tile_map, "set_cell", coords, tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i]));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, coords, tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
}
undo_redo->commit_action();
} break;
@@ -1301,13 +1325,15 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
return;
}
+ ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
+
memdelete(selection_pattern);
TypedArray<Vector2i> coords_array;
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
coords_array.push_back(E->get());
}
- selection_pattern = tile_map->get_pattern(coords_array);
+ selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
}
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() {
@@ -1344,8 +1370,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Organize using coordinates.
- for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) {
- const TileMapCell *current = E_cell->get();
+ for (const TileMapCell *current : E_source->get()) {
if (current->alternative_tile == 0) {
organized_pattern[current->get_atlas_coords()] = current;
} else {
@@ -1364,8 +1389,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
} else {
// Add everything unorganized.
- for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) {
- unorganized.push_back(E_cell->get());
+ for (const TileMapCell *cell : E_source->get()) {
+ unorganized.push_back(cell);
}
}
@@ -1375,8 +1400,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
Vector2i organized_size = selection_pattern->get_size();
int unorganized_index = 0;
- for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) {
- selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ for (const TileMapCell *cell : unorganized) {
+ selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), cell->source_id, cell->get_atlas_coords(), cell->alternative_tile);
unorganized_index++;
}
vertical_offset += MAX(organized_size.y, 1);
@@ -1711,13 +1736,19 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const {
return output;
}
-void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) {
- tile_map_id = p_tile_map_id;
+void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
+ _stop_dragging(); // Avoids staying in a wrong drag state.
- // Clear the selection.
- tile_set_selection.clear();
- tile_map_selection.clear();
- selection_pattern->clear();
+ if (tile_map_id != p_tile_map_id) {
+ tile_map_id = p_tile_map_id;
+
+ // Clear the selection.
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+ }
+
+ tile_map_layer = p_tile_map_layer;
}
void TileMapEditorTilesPlugin::_bind_methods() {
@@ -2299,6 +2330,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c
}
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>());
+ ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>());
// Build a set of dummy constraints get the constrained points.
Set<Constraint> dummy_constraints;
@@ -2322,7 +2354,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c
Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) {
if (!p_to_replace.has(E_overlapping->key())) {
- TileMapCell neighbor_cell = tile_map->get_cell(E_overlapping->key());
+ TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping->key());
TileData *neighbor_tile_data = nullptr;
if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
neighbor_tile_data = terrain_tiles[neighbor_cell];
@@ -2629,6 +2661,90 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
return output;
}
+void TileMapEditorTerrainsPlugin::_stop_dragging() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
+
+ switch (drag_type) {
+ case DRAG_TYPE_PICK: {
+ Vector2i coords = tile_map->world_to_map(mpos);
+ TileMapCell tile = tile_map->get_cell(tile_map_layer, coords);
+
+ if (terrain_tiles.has(tile)) {
+ Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
+
+ // Find the tree item for the right terrain set.
+ bool need_tree_item_switch = true;
+ TreeItem *tree_item = terrains_tree->get_selected();
+ if (tree_item) {
+ Dictionary metadata_dict = tree_item->get_metadata(0);
+ if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
+ int terrain_set = metadata_dict["terrain_set"];
+ int terrain_id = metadata_dict["terrain_id"];
+ if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ need_tree_item_switch = false;
+ }
+ }
+ }
+
+ if (need_tree_item_switch) {
+ for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) {
+ Dictionary metadata_dict = tree_item->get_metadata(0);
+ if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
+ int terrain_set = metadata_dict["terrain_set"];
+ int terrain_id = metadata_dict["terrain_id"];
+ if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ // Found
+ tree_item->select(0);
+ _update_tiles_list();
+ break;
+ }
+ }
+ }
+ }
+
+ // Find the list item for the given tile.
+ if (tree_item) {
+ for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
+ Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
+ TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+ bool equals = true;
+ for (int j = 0; j < terrains_tile_pattern.size(); j++) {
+ if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
+ equals = false;
+ break;
+ }
+ }
+ if (equals) {
+ terrains_tile_list->select(i);
+ break;
+ }
+ }
+ } else {
+ ERR_PRINT("Terrain tile not found.");
+ }
+ }
+ picker_button->set_pressed(false);
+ } break;
+ case DRAG_TYPE_PAINT: {
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+ default:
+ break;
+ }
+ drag_type = DRAG_TYPE_NONE;
+}
+
bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
if (!is_visible_in_tree()) {
// If the bottom editor is not visible, we ignore inputs.
@@ -2649,6 +2765,11 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
return false;
}
+ if (tile_map_layer < 0) {
+ return false;
+ }
+ ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
+
// Get the selected terrain.
TerrainsTilePattern selected_terrains_tile_pattern;
int selected_terrain_set = -1;
@@ -2690,9 +2811,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) {
if (!drag_modified.has(E->key())) {
- drag_modified[E->key()] = tile_map->get_cell(E->key());
+ drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
}
- tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
}
} break;
@@ -2728,86 +2849,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- drag_modified[E->key()] = tile_map->get_cell(E->key());
- tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
+ tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
}
}
}
} else {
// Released
- switch (drag_type) {
- case DRAG_TYPE_PICK: {
- Vector2i coords = tile_map->world_to_map(mpos);
- TileMapCell tile = tile_map->get_cell(coords);
-
- if (terrain_tiles.has(tile)) {
- Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
-
- // Find the tree item for the right terrain set.
- bool need_tree_item_switch = true;
- TreeItem *tree_item = terrains_tree->get_selected();
- if (tree_item) {
- Dictionary metadata_dict = tree_item->get_metadata(0);
- if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
- int terrain_set = metadata_dict["terrain_set"];
- int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
- need_tree_item_switch = false;
- }
- }
- }
-
- if (need_tree_item_switch) {
- for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) {
- Dictionary metadata_dict = tree_item->get_metadata(0);
- if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
- int terrain_set = metadata_dict["terrain_set"];
- int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
- // Found
- tree_item->select(0);
- _update_tiles_list();
- break;
- }
- }
- }
- }
-
- // Find the list item for the given tile.
- if (tree_item) {
- for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
- Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
- bool equals = true;
- for (int j = 0; j < terrains_tile_pattern.size(); j++) {
- if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
- equals = false;
- break;
- }
- }
- if (equals) {
- terrains_tile_list->select(i);
- break;
- }
- }
- } else {
- ERR_PRINT("Terrain tile not found.");
- }
- }
- picker_button->set_pressed(false);
- } break;
- case DRAG_TYPE_PAINT: {
- undo_redo->create_action(TTR("Paint terrain"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- }
- undo_redo->commit_action(false);
- } break;
- default:
- break;
- }
- drag_type = DRAG_TYPE_NONE;
+ _stop_dragging();
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -3080,8 +3129,12 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
}
}
-void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id) {
+void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
+ _stop_dragging(); // Avoids staying in a wrong drag state.
+
tile_map_id = p_tile_map_id;
+ tile_map_layer = p_tile_map_layer;
+
_update_terrains_cache();
_update_terrains_tree();
_update_tiles_list();
@@ -3166,10 +3219,12 @@ void TileMapEditor::_notification(int p_what) {
advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons")));
toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
+ toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons")));
break;
case NOTIFICATION_INTERNAL_PROCESS:
if (is_visible_in_tree() && tileset_changed_needs_update) {
_update_bottom_panel();
+ _update_layers_selection();
tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed();
CanvasItemEditor::get_singleton()->update_viewport();
tileset_changed_needs_update = false;
@@ -3178,6 +3233,16 @@ void TileMapEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED:
toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
break;
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ if (is_visible_in_tree()) {
+ tile_map->set_selected_layer(tile_map_layer);
+ } else {
+ tile_map->set_selected_layer(-1);
+ }
+ }
+ break;
}
}
@@ -3185,6 +3250,63 @@ void TileMapEditor::_on_grid_toggled(bool p_pressed) {
EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed);
}
+void TileMapEditor::_layers_selection_button_draw() {
+ if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
+ return;
+ }
+
+ RID ci = layers_selection_button->get_canvas_item();
+ Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
+
+ Color clr = Color(1, 1, 1);
+ if (get_theme_constant(SNAME("modulate_arrow"))) {
+ switch (layers_selection_button->get_draw_mode()) {
+ case BaseButton::DRAW_PRESSED:
+ clr = get_theme_color(SNAME("font_pressed_color"));
+ break;
+ case BaseButton::DRAW_HOVER:
+ clr = get_theme_color(SNAME("font_hover_color"));
+ break;
+ case BaseButton::DRAW_DISABLED:
+ clr = get_theme_color(SNAME("font_disabled_color"));
+ break;
+ default:
+ clr = get_theme_color(SNAME("font_color"));
+ }
+ }
+
+ Size2 size = layers_selection_button->get_size();
+
+ Point2 ofs;
+ if (is_layout_rtl()) {
+ ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
+ } else {
+ ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
+ }
+ Rect2 dst_rect = Rect2(ofs, arrow->get_size());
+ if (!layers_selection_button->is_pressed()) {
+ dst_rect.size = -dst_rect.size;
+ }
+ arrow->draw_rect(ci, dst_rect, false, clr);
+}
+
+void TileMapEditor::_layers_selection_button_pressed() {
+ if (!layers_selection_popup->is_visible()) {
+ Size2 size = layers_selection_popup->get_contents_minimum_size();
+ size.x = MAX(size.x, layers_selection_button->get_size().x);
+ layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y));
+ layers_selection_popup->set_size(size);
+ layers_selection_popup->popup();
+ } else {
+ layers_selection_popup->hide();
+ }
+}
+
+void TileMapEditor::_layers_selection_id_pressed(int p_id) {
+ tile_map_layer = p_id;
+ _update_layers_selection();
+}
+
void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -3198,18 +3320,20 @@ void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) {
if (p_id == 0) { // Replace Tile Proxies
undo_redo->create_action(TTR("Replace Tiles with Proxies"));
- TypedArray<Vector2i> used_cells = tile_map->get_used_cells();
- for (int i = 0; i < used_cells.size(); i++) {
- Vector2i cell_coords = used_cells[i];
- TileMapCell from = tile_map->get_cell(cell_coords);
- Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
- TileMapCell to;
- to.source_id = to_array[0];
- to.set_atlas_coords(to_array[1]);
- to.alternative_tile = to_array[2];
- if (from != to) {
- undo_redo->add_do_method(tile_map, "set_cell", cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile);
+ for (int layer_index = 0; layer_index < tile_map->get_layers_count(); layer_index++) {
+ TypedArray<Vector2i> used_cells = tile_map->get_used_cells(layer_index);
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i cell_coords = used_cells[i];
+ TileMapCell from = tile_map->get_cell(layer_index, cell_coords);
+ Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
+ TileMapCell to;
+ to.source_id = to_array[0];
+ to.set_atlas_coords(to_array[1]);
+ to.alternative_tile = to_array[2];
+ if (from != to) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile);
+ }
}
}
undo_redo->commit_action();
@@ -3315,7 +3439,7 @@ void TileMapEditor::_tile_map_changed() {
void TileMapEditor::_tab_changed(int p_tab_id) {
// Make the plugin edit the correct tilemap.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id);
+ tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
// Update toolbar.
for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
@@ -3339,7 +3463,129 @@ void TileMapEditor::_tab_changed(int p_tab_id) {
CanvasItemEditor::get_singleton()->update_viewport();
}
+void TileMapEditor::_layers_select_next_or_previous(bool p_next) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ if (tile_map->get_layers_count() < 1) {
+ return;
+ }
+
+ if (tile_map_layer < 0) {
+ tile_map_layer = 0;
+ }
+
+ int inc = p_next ? 1 : -1;
+ int origin_layer = tile_map_layer;
+ tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count());
+ while (tile_map_layer != origin_layer) {
+ if (tile_map->is_layer_enabled(tile_map_layer)) {
+ break;
+ }
+ tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count());
+ }
+
+ _update_layers_selection();
+}
+
+void TileMapEditor::_update_layers_selection() {
+ layers_selection_popup->clear();
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ // Update the selected layer.
+ if (is_visible_in_tree() && tile_map->get_layers_count() >= 1) {
+ tile_map_layer = CLAMP(tile_map_layer, 0, tile_map->get_layers_count() - 1);
+
+ // Search for an enabled layer if the current one is not.
+ int origin_layer = tile_map_layer;
+ while (tile_map_layer >= 0 && !tile_map->is_layer_enabled(tile_map_layer)) {
+ tile_map_layer--;
+ }
+ if (tile_map_layer < 0) {
+ tile_map_layer = origin_layer;
+ while (tile_map_layer < tile_map->get_layers_count() && !tile_map->is_layer_enabled(tile_map_layer)) {
+ tile_map_layer++;
+ }
+ }
+ if (tile_map_layer >= tile_map->get_layers_count()) {
+ tile_map_layer = -1;
+ }
+ } else {
+ tile_map_layer = -1;
+ }
+ tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
+
+ // Build the list of layers.
+ for (int i = 0; i < tile_map->get_layers_count(); i++) {
+ String name = tile_map->get_layer_name(i);
+ layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i);
+ layers_selection_popup->set_item_as_radio_checkable(i, true);
+ layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i));
+ layers_selection_popup->set_item_checked(i, i == tile_map_layer);
+ }
+
+ // Update the button label.
+ if (tile_map_layer >= 0) {
+ layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer));
+ } else {
+ layers_selection_button->set_text(TTR("Select a layer"));
+ }
+
+ // Set button minimum width.
+ Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0);
+ if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
+ Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
+ min_button_size.x += arrow->get_size().x;
+ }
+ layers_selection_button->set_custom_minimum_size(min_button_size);
+ layers_selection_button->update();
+
+ tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+}
+
+void TileMapEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+ TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
+ if (tile_map) {
+ if (p_property == "layers_count") {
+ int new_layers_count = (int)p_new_value;
+ if (new_layers_count < tile_map->get_layers_count()) {
+ List<PropertyInfo> property_list;
+ tile_map->get_property_list(&property_list);
+
+ for (PropertyInfo property_info : property_list) {
+ Vector<String> components = String(property_info.name).split("/", true, 2);
+ if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
+ int index = components[0].trim_prefix("layer_").to_int();
+ if (index >= new_layers_count) {
+ undo_redo->add_undo_property(tile_map, property_info.name, tile_map->get(property_info.name));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
+ if (ED_IS_SHORTCUT("tiles_editor/select_next_layer", p_event) && p_event->is_pressed()) {
+ _layers_select_next_or_previous(true);
+ return true;
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/select_previous_layer", p_event) && p_event->is_pressed()) {
+ _layers_select_next_or_previous(false);
+ return true;
+ }
+
return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event);
}
@@ -3363,49 +3609,52 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
Vector2i tile_shape_size = tile_set->get_tile_size();
// Draw tiles with invalid IDs in the grid.
- TypedArray<Vector2i> used_cells = tile_map->get_used_cells();
- for (int i = 0; i < used_cells.size(); i++) {
- Vector2i coords = used_cells[i];
- int tile_source_id = tile_map->get_cell_source_id(coords);
- if (tile_source_id >= 0) {
- Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(coords);
- int tile_alternative_tile = tile_map->get_cell_alternative_tile(coords);
-
- TileSetSource *source = nullptr;
- if (tile_set->has_source(tile_source_id)) {
- source = *tile_set->get_source(tile_source_id);
- }
-
- if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) {
- // Generate a random color from the hashed values of the tiles.
- Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile);
- if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) {
- // Only display the pattern if we have no proxy tile.
- Array to_hash;
- to_hash.push_back(tile_source_id);
- to_hash.push_back(tile_atlas_coords);
- to_hash.push_back(tile_alternative_tile);
- uint32_t hash = RandomPCG(to_hash.hash()).rand();
-
- Color color;
- color = color.from_hsv(
- (float)((hash >> 24) & 0xFF) / 256.0,
- Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
- Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
- 0.8);
-
- // Draw the scaled tile.
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size)));
- tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture);
+ if (tile_map_layer >= 0) {
+ ERR_FAIL_COND(tile_map_layer >= tile_map->get_layers_count());
+ TypedArray<Vector2i> used_cells = tile_map->get_used_cells(tile_map_layer);
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = used_cells[i];
+ int tile_source_id = tile_map->get_cell_source_id(tile_map_layer, coords);
+ if (tile_source_id >= 0) {
+ Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(tile_map_layer, coords);
+ int tile_alternative_tile = tile_map->get_cell_alternative_tile(tile_map_layer, coords);
+
+ TileSetSource *source = nullptr;
+ if (tile_set->has_source(tile_source_id)) {
+ source = *tile_set->get_source(tile_source_id);
}
- // Draw the warning icon.
- int min_axis = missing_tile_texture->get_size().min_axis();
- Vector2 icon_size;
- icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3;
- icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]);
- Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale());
- p_overlay->draw_texture_rect(missing_tile_texture, rect);
+ if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) {
+ // Generate a random color from the hashed values of the tiles.
+ Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile);
+ if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) {
+ // Only display the pattern if we have no proxy tile.
+ Array to_hash;
+ to_hash.push_back(tile_source_id);
+ to_hash.push_back(tile_atlas_coords);
+ to_hash.push_back(tile_alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw the scaled tile.
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size)));
+ tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture);
+ }
+
+ // Draw the warning icon.
+ int min_axis = missing_tile_texture->get_size().min_axis();
+ Vector2 icon_size;
+ icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3;
+ icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]);
+ Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale());
+ p_overlay->draw_texture_rect(missing_tile_texture, rect);
+ }
}
}
}
@@ -3476,14 +3725,19 @@ void TileMapEditor::edit(TileMap *p_tile_map) {
return;
}
- // Disconnect to changes.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
+ // Unselect layer if we are changing tile_map.
+ if (tile_map != p_tile_map) {
+ tile_map->set_selected_layer(-1);
+ }
+
+ // Disconnect to changes.
tile_map->disconnect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed));
}
- // Change the edited object.
if (p_tile_map) {
+ // Change the edited object.
tile_map_id = p_tile_map->get_instance_id();
tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
// Connect to changes.
@@ -3494,8 +3748,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) {
tile_map_id = ObjectID();
}
+ _update_layers_selection();
+
// Call the plugins.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id);
+ tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
_tile_map_changed();
}
@@ -3503,6 +3759,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) {
TileMapEditor::TileMapEditor() {
set_process_internal(true);
+ // Shortcuts.
+ ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), KEY_PAGEUP);
+ ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), KEY_PAGEDOWN);
+
// TileMap editor plugins
tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin));
tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin));
@@ -3516,18 +3776,46 @@ TileMapEditor::TileMapEditor() {
tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
// --- TileMap toolbar ---
- tilemap_toolbar = memnew(HBoxContainer);
- tilemap_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_toolbar->add_child(tabs);
+ tile_map_toolbar = memnew(HBoxContainer);
+ tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ // Tabs.
+ tile_map_toolbar->add_child(tabs);
+
+ // Tabs toolbars.
for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
tile_map_editor_plugins[i]->get_toolbar()->hide();
- tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
+ tile_map_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
}
// Wide empty separation control.
Control *h_empty_space = memnew(Control);
h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_toolbar->add_child(h_empty_space);
+ tile_map_toolbar->add_child(h_empty_space);
+
+ // Layer selector.
+ layers_selection_popup = memnew(PopupMenu);
+ layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed));
+ layers_selection_popup->set_close_on_parent_focus(false);
+
+ layers_selection_button = memnew(Button);
+ layers_selection_button->set_toggle_mode(true);
+ layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw));
+ layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed));
+ layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide));
+ layers_selection_button->set_tooltip(TTR("Tile Map Layer"));
+ layers_selection_button->add_child(layers_selection_popup);
+ tile_map_toolbar->add_child(layers_selection_button);
+
+ toogle_highlight_selected_layer_button = memnew(Button);
+ toogle_highlight_selected_layer_button->set_flat(true);
+ toogle_highlight_selected_layer_button->set_toggle_mode(true);
+ toogle_highlight_selected_layer_button->set_pressed(true);
+ toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
+ toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer"));
+ tile_map_toolbar->add_child(toogle_highlight_selected_layer_button);
+
+ tile_map_toolbar->add_child(memnew(VSeparator));
// Grid toggle.
toggle_grid_button = memnew(Button);
@@ -3535,14 +3823,14 @@ TileMapEditor::TileMapEditor() {
toggle_grid_button->set_toggle_mode(true);
toggle_grid_button->set_tooltip(TTR("Toggle grid visibility."));
toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditor::_on_grid_toggled));
- tilemap_toolbar->add_child(toggle_grid_button);
+ tile_map_toolbar->add_child(toggle_grid_button);
// Advanced settings menu button.
advanced_menu_button = memnew(MenuButton);
advanced_menu_button->set_flat(true);
advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies"));
advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileMapEditor::_advanced_menu_button_id_pressed));
- tilemap_toolbar->add_child(advanced_menu_button);
+ tile_map_toolbar->add_child(advanced_menu_button);
missing_tileset_label = memnew(Label);
missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource."));
@@ -3561,6 +3849,9 @@ TileMapEditor::TileMapEditor() {
}
_tab_changed(0);
+
+ // Registers UndoRedo inspector callback.
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileMapEditor::_undo_redo_inspector_callback));
}
TileMapEditor::~TileMapEditor() {
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 236774a06b..6e2f2ce2ba 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -47,7 +47,7 @@ public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
virtual void tile_set_changed(){};
- virtual void edit(ObjectID p_tile_map_id){};
+ virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer){};
};
class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
@@ -56,7 +56,8 @@ class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
ObjectID tile_map_id;
- virtual void edit(ObjectID p_tile_map_id) override;
+ int tile_map_layer = -1;
+ virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
///// Toolbar /////
HBoxContainer *toolbar;
@@ -185,7 +186,8 @@ class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin {
private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
ObjectID tile_map_id;
- virtual void edit(ObjectID p_tile_map_id) override;
+ int tile_map_layer = -1;
+ virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
// Toolbar.
HBoxContainer *toolbar;
@@ -258,9 +260,9 @@ private:
Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const;
Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const;
+ void _stop_dragging();
// Cached data.
-
TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data);
LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles;
LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns;
@@ -300,12 +302,20 @@ private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
bool tileset_changed_needs_update = false;
ObjectID tile_map_id;
+ int tile_map_layer = -1;
// Vector to keep plugins.
Vector<TileMapEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
- HBoxContainer *tilemap_toolbar;
+ HBoxContainer *tile_map_toolbar;
+
+ PopupMenu *layers_selection_popup;
+ Button *layers_selection_button;
+ Button *toogle_highlight_selected_layer_button;
+ void _layers_selection_button_draw();
+ void _layers_selection_button_pressed();
+ void _layers_selection_id_pressed(int p_id);
Button *toggle_grid_button;
void _on_grid_toggled(bool p_pressed);
@@ -313,19 +323,26 @@ private:
MenuButton *advanced_menu_button;
void _advanced_menu_button_id_pressed(int p_id);
- // Bottom panel
+ // Bottom panel.
Label *missing_tileset_label;
Tabs *tabs;
void _update_bottom_panel();
- // TileMap
+ // TileMap.
Ref<Texture2D> missing_tile_texture;
Ref<Texture2D> warning_pattern_texture;
- // CallBack
+ // CallBack.
void _tile_map_changed();
void _tab_changed(int p_tab_changed);
+ // Updates.
+ void _layers_select_next_or_previous(bool p_next);
+ void _update_layers_selection();
+
+ // Inspector undo/redo callback.
+ void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
+
protected:
void _notification(int p_what);
void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
@@ -335,7 +352,7 @@ public:
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMap *p_tile_map);
- Control *get_toolbar() { return tilemap_toolbar; };
+ Control *get_toolbar() { return tile_map_toolbar; };
TileMapEditor();
~TileMapEditor();
diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h
index f6898e960b..6849be2cd6 100644
--- a/editor/plugins/tiles/tile_proxies_manager_dialog.h
+++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h
@@ -34,9 +34,9 @@
#include "editor/editor_node.h"
#include "editor/editor_properties.h"
+#include "scene/2d/tile_map.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
-#include "scene/resources/tile_set.h"
class TileProxiesManagerDialog : public ConfirmationDialog {
GDCLASS(TileProxiesManagerDialog, ConfirmationDialog);
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index e9efa1cf84..6afa0c0779 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -34,6 +34,8 @@
#include "core/math/geometry_2d.h"
#include "core/os/os.h"
+#include "servers/navigation_server_2d.h"
+
void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords));
@@ -235,40 +237,42 @@ Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffset
return output;
}
-int TileMap::get_effective_quadrant_size() const {
+int TileMap::get_effective_quadrant_size(int p_layer) const {
// When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant
- if (tile_set.is_valid() && tile_set->is_y_sorting()) {
+ if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) {
return 1;
} else {
return quadrant_size;
}
}
-Vector2i TileMap::_coords_to_quadrant_coords(const Vector2i &p_coords) const {
- int quadrant_size = get_effective_quadrant_size();
+void TileMap::set_selected_layer(int p_layer_id) {
+ ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size());
+ selected_layer = p_layer_id;
+ emit_signal(SNAME("changed"));
+ _make_all_quadrants_dirty();
+}
- // Rounding down, instead of simply rounding towards zero (truncating)
- return Vector2i(
- p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size,
- p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size);
+int TileMap::get_selected_layer() const {
+ return selected_layer;
}
void TileMap::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
pending_update = true;
- _recreate_quadrants();
+ _recreate_internals();
} break;
case NOTIFICATION_EXIT_TREE: {
- _clear_quadrants();
+ _clear_internals();
} break;
}
// Transfers the notification to tileset plugins.
if (tile_set.is_valid()) {
- for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
- tile_set->get_tile_set_atlas_plugins()[i]->tilemap_notification(this, p_what);
- }
+ _rendering_notification(p_what);
+ _physics_notification(p_what);
+ _navigation_notification(p_what);
}
}
@@ -283,64 +287,210 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
// Set the tileset, registering to its changes.
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty));
tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
}
if (!p_tileset.is_valid()) {
- _clear_quadrants();
+ _clear_internals();
}
tile_set = p_tileset;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty), varray(true));
tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed));
- _recreate_quadrants();
+ _recreate_internals();
}
emit_signal(SNAME("changed"));
}
+void TileMap::set_quadrant_size(int p_size) {
+ ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
+
+ quadrant_size = p_size;
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+}
+
int TileMap::get_quadrant_size() const {
return quadrant_size;
}
-void TileMap::set_quadrant_size(int p_size) {
- ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
+void TileMap::set_layers_count(int p_layers_count) {
+ ERR_FAIL_COND(p_layers_count < 0);
+ _clear_internals();
- quadrant_size = p_size;
- _recreate_quadrants();
+ layers.resize(p_layers_count);
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer >= p_layers_count) {
+ selected_layer = -1;
+ }
+
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+int TileMap::get_layers_count() const {
+ return layers.size();
+}
+
+void TileMap::set_layer_name(int p_layer, String p_name) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].name = p_name;
+ emit_signal(SNAME("changed"));
+}
+
+String TileMap::get_layer_name(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), String());
+ return layers[p_layer].name;
+}
+
+void TileMap::set_layer_enabled(int p_layer, bool p_enabled) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].enabled = p_enabled;
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+bool TileMap::is_layer_enabled(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
+ return layers[p_layer].enabled;
+}
+
+void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].y_sort_enabled = p_y_sort_enabled;
+ _recreate_internals();
emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
+ return layers[p_layer].y_sort_enabled;
+}
+
+void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].y_sort_origin = p_y_sort_origin;
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+}
+
+int TileMap::get_layer_y_sort_origin(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
+ return layers[p_layer].y_sort_origin;
+}
+
+void TileMap::set_layer_z_index(int p_layer, int p_z_index) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].z_index = p_z_index;
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+
+ update_configuration_warnings();
+}
+
+int TileMap::get_layer_z_index(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
+ return layers[p_layer].z_index;
}
void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
- show_collision = p_show_collision;
- _recreate_quadrants();
+ collision_visibility_mode = p_show_collision;
+ _recreate_internals();
emit_signal(SNAME("changed"));
}
TileMap::VisibilityMode TileMap::get_collision_visibility_mode() {
- return show_collision;
+ return collision_visibility_mode;
}
void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) {
- show_navigation = p_show_navigation;
- _recreate_quadrants();
+ navigation_visibility_mode = p_show_navigation;
+ _recreate_internals();
emit_signal(SNAME("changed"));
}
TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
- return show_navigation;
+ return navigation_visibility_mode;
}
void TileMap::set_y_sort_enabled(bool p_enable) {
Node2D::set_y_sort_enabled(p_enable);
- _recreate_quadrants();
+ _recreate_internals();
emit_signal(SNAME("changed"));
}
-void TileMap::update_dirty_quadrants() {
+Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const {
+ int quadrant_size = get_effective_quadrant_size(p_layer);
+
+ // Rounding down, instead of simply rounding towards zero (truncating)
+ return Vector2i(
+ p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size,
+ p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size);
+}
+
+Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
+
+ TileMapQuadrant q;
+ q.layer = p_layer;
+ q.coords = p_qk;
+
+ rect_cache_dirty = true;
+
+ // Create the debug canvas item.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ q.debug_canvas_item = rs->canvas_item_create();
+ rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
+ rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item());
+
+ // Call the create_quadrant method on plugins
+ if (tile_set.is_valid()) {
+ _rendering_create_quadrant(&q);
+ _physics_create_quadrant(&q);
+ }
+
+ return layers[p_layer].quadrant_map.insert(p_qk, q);
+}
+
+void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) {
+ // Make the given quadrant dirty, then trigger an update later.
+ TileMapQuadrant &q = Q->get();
+ if (!q.dirty_list_element.in_list()) {
+ layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element);
+ }
+ _queue_update_dirty_quadrants();
+}
+
+void TileMap::_make_all_quadrants_dirty() {
+ // Make all quandrants dirty, then trigger an update later.
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ if (!E->value().dirty_list_element.in_list()) {
+ layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element);
+ }
+ }
+ }
+ _queue_update_dirty_quadrants();
+}
+
+void TileMap::_queue_update_dirty_quadrants() {
+ if (pending_update || !is_inside_tree()) {
+ return;
+ }
+ pending_update = true;
+ call_deferred(SNAME("_update_dirty_quadrants"));
+}
+
+void TileMap::_update_dirty_quadrants() {
if (!pending_update) {
return;
}
@@ -349,43 +499,130 @@ void TileMap::update_dirty_quadrants() {
return;
}
- // Update the coords cache.
- for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
- q->self()->map_to_world.clear();
- q->self()->world_to_map.clear();
- for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
- Vector2i pk = E->get();
- Vector2i pk_world_coords = map_to_world(pk);
- q->self()->map_to_world[pk] = pk_world_coords;
- q->self()->world_to_map[pk_world_coords] = pk;
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ // Update the coords cache.
+ for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) {
+ q->self()->map_to_world.clear();
+ q->self()->world_to_map.clear();
+ for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
+ Vector2i pk = E->get();
+ Vector2i pk_world_coords = map_to_world(pk);
+ q->self()->map_to_world[pk] = pk_world_coords;
+ q->self()->world_to_map[pk_world_coords] = pk;
+ }
+ }
+
+ // Call the update_dirty_quadrant method on plugins.
+ _rendering_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
+ _physics_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
+ _navigation_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
+ _scenes_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
+
+ // Redraw the debug canvas_items.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) {
+ rs->canvas_item_clear(q->self()->debug_canvas_item);
+ Transform2D xform;
+ xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer)));
+ rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform);
+
+ _rendering_draw_quadrant_debug(q->self());
+ _physics_draw_quadrant_debug(q->self());
+ _navigation_draw_quadrant_debug(q->self());
+ _scenes_draw_quadrant_debug(q->self());
}
- }
- // Call the update_dirty_quadrant method on plugins.
- for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
- tile_set->get_tile_set_atlas_plugins()[i]->update_dirty_quadrants(this, dirty_quadrant_list);
+ // Clear the list
+ while (layers[layer].dirty_quadrant_list.first()) {
+ layers[layer].dirty_quadrant_list.remove(layers[layer].dirty_quadrant_list.first());
+ }
}
- // Redraw the debug canvas_items.
- RenderingServer *rs = RenderingServer::get_singleton();
- for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
- rs->canvas_item_clear(q->self()->debug_canvas_item);
- Transform2D xform;
- xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size()));
- rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform);
- for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
- tile_set->get_tile_set_atlas_plugins()[i]->draw_quadrant_debug(this, q->self());
+ pending_update = false;
+
+ _recompute_rect_cache();
+}
+
+void TileMap::_recreate_internals() {
+ // Clear all internals.
+ _clear_internals();
+
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ if (!layers[layer].enabled) {
+ continue;
+ }
+
+ // Upadate the layer internals.
+ _rendering_update_layer(layer);
+
+ // Recreate the quadrants.
+ const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map;
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y));
+
+ Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk);
+ if (!Q) {
+ Q = _create_quadrant(layer, qk);
+ layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element);
+ }
+
+ Vector2i pk = E->key();
+ Q->get().cells.insert(pk);
+
+ _make_quadrant_dirty(Q);
}
}
- // Clear the list
- while (dirty_quadrant_list.first()) {
- dirty_quadrant_list.remove(dirty_quadrant_list.first());
+ _update_dirty_quadrants();
+}
+
+void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
+ // Remove a quadrant.
+ TileMapQuadrant *q = &(Q->get());
+
+ // Call the cleanup_quadrant method on plugins.
+ if (tile_set.is_valid()) {
+ _rendering_cleanup_quadrant(q);
+ _physics_cleanup_quadrant(q);
+ _navigation_cleanup_quadrant(q);
+ _scenes_cleanup_quadrant(q);
}
- pending_update = false;
+ // Remove the quadrant from the dirty_list if it is there.
+ if (q->dirty_list_element.in_list()) {
+ layers[q->layer].dirty_quadrant_list.remove(&(q->dirty_list_element));
+ }
- _recompute_rect_cache();
+ // Free the debug canvas item.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ rs->free(q->debug_canvas_item);
+
+ layers[q->layer].quadrant_map.erase(Q);
+ rect_cache_dirty = true;
+}
+
+void TileMap::_clear_layer_internals(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ // Clear quadrants.
+ while (layers[p_layer].quadrant_map.size()) {
+ _erase_quadrant(layers[p_layer].quadrant_map.front());
+ }
+
+ // Clear the layers internals.
+ _rendering_cleanup_layer(p_layer);
+
+ // Clear the dirty quadrants list.
+ while (layers[p_layer].dirty_quadrant_list.first()) {
+ layers[p_layer].dirty_quadrant_list.remove(layers[p_layer].dirty_quadrant_list.first());
+ }
+}
+
+void TileMap::_clear_internals() {
+ // Clear quadrants.
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ _clear_layer_internals(layer);
+ }
}
void TileMap::_recompute_rect_cache() {
@@ -397,16 +634,18 @@ void TileMap::_recompute_rect_cache() {
}
Rect2 r_total;
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Rect2 r;
- r.position = map_to_world(E->key() * get_effective_quadrant_size());
- r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size()));
- r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size()));
- r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size()));
- if (E == quadrant_map.front()) {
- r_total = r;
- } else {
- r_total = r_total.merge(r);
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ Rect2 r;
+ r.position = map_to_world(E->key() * get_effective_quadrant_size(layer));
+ r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer)));
+ r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size(layer)));
+ r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size(layer)));
+ if (E == layers[layer].quadrant_map.front()) {
+ r_total = r;
+ } else {
+ r_total = r_total.merge(r);
+ }
}
}
@@ -418,94 +657,884 @@ void TileMap::_recompute_rect_cache() {
#endif
}
-Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(const Vector2i &p_qk) {
- TileMapQuadrant q;
- q.coords = p_qk;
+/////////////////////////////// Rendering //////////////////////////////////////
- rect_cache_dirty = true;
+void TileMap::_rendering_notification(int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+ bool visible = is_visible_in_tree();
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+
+ // Update occluders transform.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ for (const RID &occluder : q.occluders) {
+ RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible);
+ }
+ }
+ }
+ }
+ } break;
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ if (!is_inside_tree()) {
+ return;
+ }
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+
+ // Update occluders transform.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ for (const RID &occluder : q.occluders) {
+ RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform);
+ }
+ }
+ }
+ }
+ } break;
+ case CanvasItem::NOTIFICATION_DRAW: {
+ if (tile_set.is_valid()) {
+ RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled());
+ }
+ } break;
+ }
+}
+
+void TileMap::_rendering_update_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
- // Create the debug canvas item.
RenderingServer *rs = RenderingServer::get_singleton();
- q.debug_canvas_item = rs->canvas_item_create();
- rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
- rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item());
+ if (!layers[p_layer].canvas_item.is_valid()) {
+ RID ci = rs->canvas_item_create();
+ rs->canvas_item_set_parent(ci, get_canvas_item());
- // Call the create_quadrant method on plugins
- if (tile_set.is_valid()) {
- for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
- tile_set->get_tile_set_atlas_plugins()[i]->create_quadrant(this, &q);
- }
+ /*Transform2D xform;
+ xform.set_origin(Vector2(0, p_layer));
+ rs->canvas_item_set_transform(ci, xform);*/
+ rs->canvas_item_set_draw_index(ci, p_layer);
+
+ layers[p_layer].canvas_item = ci;
}
+ RID &ci = layers[p_layer].canvas_item;
+ rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled);
+ rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
+ rs->canvas_item_set_z_index(ci, layers[p_layer].z_index);
+ rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter()));
+ rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat()));
+ rs->canvas_item_set_light_mask(ci, get_light_mask());
+}
- return quadrant_map.insert(p_qk, q);
+void TileMap::_rendering_cleanup_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+ if (!layers[p_layer].canvas_item.is_valid()) {
+ rs->free(layers[p_layer].canvas_item);
+ }
}
-void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
- // Remove a quadrant.
- TileMapQuadrant *q = &(Q->get());
+void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(!tile_set.is_valid());
- // Call the cleanup_quadrant method on plugins.
- if (tile_set.is_valid()) {
- for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
- tile_set->get_tile_set_atlas_plugins()[i]->cleanup_quadrant(this, q);
+ bool visible = is_visible_in_tree();
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ // Free the canvas items.
+ for (const RID &ci : q.canvas_items) {
+ rs->free(ci);
+ }
+ q.canvas_items.clear();
+
+ // Free the occluders.
+ for (const RID &occluder : q.occluders) {
+ rs->free(occluder);
}
+ q.occluders.clear();
+
+ // Those allow to group cell per material or z-index.
+ Ref<ShaderMaterial> prev_material;
+ int prev_z_index = 0;
+ RID prev_canvas_item;
+
+ // Iterate over the cells of the quadrant.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = get_cell(q.layer, E_cell->value(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get the tile data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ Ref<ShaderMaterial> mat = tile_data->tile_get_material();
+ int z_index = layers[q.layer].z_index + tile_data->get_z_index();
+
+ // Quandrant pos.
+ Vector2 position = map_to_world(q.coords * get_effective_quadrant_size(q.layer));
+ if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) {
+ // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem.
+ position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin();
+ }
+
+ // --- CanvasItems ---
+ // Create two canvas items, for rendering and debug.
+ RID canvas_item;
+
+ // Check if the material or the z_index changed.
+ if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
+ // If so, create a new CanvasItem.
+ canvas_item = rs->canvas_item_create();
+ if (mat.is_valid()) {
+ rs->canvas_item_set_material(canvas_item, mat->get_rid());
+ }
+ rs->canvas_item_set_parent(canvas_item, layers[q.layer].canvas_item);
+ rs->canvas_item_set_use_parent_material(canvas_item, get_use_parent_material() || get_material().is_valid());
+
+ Transform2D xform;
+ xform.set_origin(position);
+ rs->canvas_item_set_transform(canvas_item, xform);
+
+ rs->canvas_item_set_light_mask(canvas_item, get_light_mask());
+ rs->canvas_item_set_z_index(canvas_item, z_index);
+
+ rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(get_texture_filter()));
+ rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(get_texture_repeat()));
+
+ q.canvas_items.push_back(canvas_item);
+
+ prev_canvas_item = canvas_item;
+ prev_material = mat;
+ prev_z_index = z_index;
+
+ } else {
+ // Keep the same canvas_item to draw on.
+ canvas_item = prev_canvas_item;
+ }
+
+ // Drawing the tile in the canvas item.
+ Color modulate = get_self_modulate();
+ if (selected_layer >= 0) {
+ if (q.layer < selected_layer) {
+ modulate = modulate.darkened(0.5);
+ } else if (q.layer > selected_layer) {
+ modulate = modulate.darkened(0.5);
+ modulate.a *= 0.3;
+ }
+ }
+ draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate);
+
+ // --- Occluders ---
+ for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ if (tile_data->get_occluder(i).is_valid()) {
+ RID occluder_id = rs->canvas_light_occluder_create();
+ rs->canvas_light_occluder_set_enabled(occluder_id, visible);
+ rs->canvas_light_occluder_set_transform(occluder_id, get_global_transform() * xform);
+ rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid());
+ rs->canvas_light_occluder_attach_to_canvas(occluder_id, get_canvas());
+ rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
+ q.occluders.push_back(occluder_id);
+ }
+ }
+ }
+ }
+ }
+
+ _rendering_quadrant_order_dirty = true;
+ q_list_element = q_list_element->next();
}
- // Remove the quadrant from the dirty_list if it is there.
- if (q->dirty_list_element.in_list()) {
- dirty_quadrant_list.remove(&(q->dirty_list_element));
+ // Reset the drawing indices
+ if (_rendering_quadrant_order_dirty) {
+ int index = -(int64_t)0x80000000; //always must be drawn below children.
+
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ // Sort the quadrants coords per world coordinates
+ Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ world_to_map[map_to_world(E->key())] = E->key();
+ }
+
+ // Sort the quadrants
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = layers[layer].quadrant_map[E->value()];
+ for (const RID &ci : q.canvas_items) {
+ RS::get_singleton()->canvas_item_set_draw_index(ci, index++);
+ }
+ }
+ }
+ _rendering_quadrant_order_dirty = false;
}
+}
- // Free the debug canvas item.
+void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ _rendering_quadrant_order_dirty = true;
+}
+
+void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+ // Free the canvas items.
+ for (const RID &ci : p_quadrant->canvas_items) {
+ RenderingServer::get_singleton()->free(ci);
+ }
+ p_quadrant->canvas_items.clear();
+
+ // Free the occluders.
+ for (const RID &occluder : p_quadrant->occluders) {
+ RenderingServer::get_singleton()->free(occluder);
+ }
+ p_quadrant->occluders.clear();
+}
+
+void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+
+ // Draw a placeholder for scenes needing one.
RenderingServer *rs = RenderingServer::get_singleton();
- rs->free(q->debug_canvas_item);
+ Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true);
- quadrant_map.erase(Q);
- rect_cache_dirty = true;
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
+ // Generate a random color from the hashed values of the tiles.
+ Array to_hash;
+ to_hash.push_back(c.source_id);
+ to_hash.push_back(c.get_atlas_coords());
+ to_hash.push_back(c.alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw a placeholder tile.
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
+ }
+ }
+ }
+ }
}
-void TileMap::_make_all_quadrants_dirty(bool p_update) {
- // Make all quandrants dirty, then trigger an update later.
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- if (!E->value().dirty_list_element.in_list()) {
- dirty_quadrant_list.add(&E->value().dirty_list_element);
+void TileMap::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) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
+
+ TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get the texture.
+ Ref<Texture2D> tex = atlas_source->get_texture();
+ if (!tex.is_valid()) {
+ return;
+ }
+
+ // Check if we are in the texture, return otherwise.
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
+ return;
+ }
+
+ // Get tile data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+
+ // Compute the offset
+ Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords);
+ Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile);
+
+ // Compute the destination rectangle in the CanvasItem.
+ Rect2 dest_rect;
+ dest_rect.size = source_rect.size;
+ dest_rect.size.x += FP_ADJUST;
+ dest_rect.size.y += FP_ADJUST;
+
+ bool transpose = tile_data->get_transpose();
+ if (transpose) {
+ dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ } else {
+ dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
+ }
+
+ if (tile_data->get_flip_h()) {
+ dest_rect.size.x = -dest_rect.size.x;
+ }
+
+ if (tile_data->get_flip_v()) {
+ dest_rect.size.y = -dest_rect.size.y;
}
+
+ // Get the tile modulation.
+ Color modulate = tile_data->get_modulate();
+ modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a);
+
+ // Draw the tile.
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
}
+}
- if (pending_update) {
+/////////////////////////////// Physics //////////////////////////////////////
+
+void TileMap::_physics_notification(int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ // Update the bodies transforms.
+ if (is_inside_tree()) {
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ Transform2D global_transform = get_global_transform();
+
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+
+ Transform2D xform;
+ xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer)));
+ xform = global_transform * xform;
+
+ for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
+ PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+ }
+ }
+ }
+ } break;
+ }
+}
+
+void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ Transform2D global_transform = get_global_transform();
+ PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer));
+
+ // Clear shapes.
+ for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
+ ps->body_clear_shapes(q.bodies[body_index]);
+
+ // Position the bodies.
+ Transform2D xform;
+ xform.set_origin(quadrant_pos);
+ xform = global_transform * xform;
+ ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = get_cell(q.layer, E_cell->get(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ 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 < q.bodies.size(); body_index++) {
+ // Add the shapes again.
+ 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(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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ q_list_element = q_list_element->next();
+ }
+}
+
+void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ //Get the TileMap's gobla transform.
+ Transform2D global_transform;
+ if (is_inside_tree()) {
+ global_transform = get_global_transform();
+ }
+
+ // Clear all bodies.
+ p_quadrant->bodies.clear();
+
+ // Create the body and set its parameters.
+ for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) {
+ RID body = PhysicsServer2D::get_singleton()->body_create();
+ PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC);
+
+ PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id());
+ PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer));
+ PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer));
+
+ Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer);
+ if (!physics_material.is_valid()) {
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
+ }
+
+ if (is_inside_tree()) {
+ RID space = get_world_2d()->get_space();
+ PhysicsServer2D::get_singleton()->body_set_space(body, space);
+
+ Transform2D xform;
+ xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer)));
+ xform = global_transform * xform;
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+
+ p_quadrant->bodies.push_back(body);
+ }
+}
+
+void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+ // Remove a quadrant.
+ for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
+ PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
+ }
+ p_quadrant->bodies.clear();
+}
+
+void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ // Draw the debug collision shapes.
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!get_tree()) {
return;
}
- pending_update = true;
- if (!is_inside_tree()) {
+
+ bool show_collision = false;
+ switch (collision_visibility_mode) {
+ case TileMap::VISIBILITY_MODE_DEFAULT:
+ show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint());
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_HIDE:
+ show_collision = false;
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_SHOW:
+ show_collision = true;
+ break;
+ }
+ if (!show_collision) {
return;
}
- if (p_update) {
- call_deferred(SNAME("update_dirty_quadrants"));
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+
+ Color debug_collision_color = get_tree()->get_debug_collisions_color();
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true);
+
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+
+ if (tile_set->has_source(c.source_id)) {
+ TileSetSource *source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ 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 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);
+ }
+ }
+ }
+ }
+ }
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D());
+ }
+};
+
+/////////////////////////////// Navigation //////////////////////////////////////
+
+void TileMap::_navigation_notification(int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ if (is_inside_tree()) {
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ Transform2D tilemap_xform = get_global_transform();
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+ for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) {
+ for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) {
+ RID region = E_region->get()[layer_index];
+ if (!region.is_valid()) {
+ continue;
+ }
+ Transform2D tile_transform;
+ tile_transform.set_origin(map_to_world(E_region->key()));
+ NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
+ }
+ }
+ }
+ }
+ }
+ } break;
}
}
-void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update) {
- // Make the given quadrant dirty, then trigger an update later.
- TileMapQuadrant &q = Q->get();
- if (!q.dirty_list_element.in_list()) {
- dirty_quadrant_list.add(&q.dirty_list_element);
+void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!is_inside_tree());
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Get colors for debug.
+ SceneTree *st = SceneTree::get_singleton();
+ Color debug_navigation_color;
+ bool debug_navigation = st && st->is_debugging_navigation_hint();
+ if (debug_navigation) {
+ debug_navigation_color = st->get_debug_navigation_color();
+ }
+
+ Transform2D tilemap_xform = get_global_transform();
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ // Clear navigation shapes in the quadrant.
+ for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().size(); i++) {
+ RID region = E->get()[i];
+ if (!region.is_valid()) {
+ continue;
+ }
+ NavigationServer2D::get_singleton()->region_set_map(region, RID());
+ }
+ }
+ q.navigation_regions.clear();
+
+ // Get the navigation polygons and create regions.
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = get_cell(q.layer, E_cell->get(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
+
+ for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
+ Ref<NavigationPolygon> navpoly;
+ navpoly = tile_data->get_navigation_polygon(layer_index);
+
+ if (navpoly.is_valid()) {
+ Transform2D tile_transform;
+ tile_transform.set_origin(map_to_world(E_cell->get()));
+
+ RID region = NavigationServer2D::get_singleton()->region_create();
+ NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
+ NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
+ NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly);
+ q.navigation_regions[E_cell->get()].write[layer_index] = region;
+ }
+ }
+ }
+ }
+ }
+
+ q_list_element = q_list_element->next();
}
+}
- if (pending_update) {
+void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+ // Clear navigation shapes in the quadrant.
+ for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().size(); i++) {
+ RID region = E->get()[i];
+ if (!region.is_valid()) {
+ continue;
+ }
+ NavigationServer2D::get_singleton()->free(region);
+ }
+ }
+ p_quadrant->navigation_regions.clear();
+}
+
+void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ // Draw the debug collision shapes.
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!get_tree()) {
return;
}
- pending_update = true;
- if (!is_inside_tree()) {
+
+ bool show_navigation = false;
+ switch (navigation_visibility_mode) {
+ case TileMap::VISIBILITY_MODE_DEFAULT:
+ show_navigation = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint());
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_HIDE:
+ show_navigation = false;
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_SHOW:
+ show_navigation = true;
+ break;
+ }
+ if (!show_navigation) {
+ return;
+ }
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ Color color = get_tree()->get_debug_navigation_color();
+ RandomPCG rand;
+
+ Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+
+ for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
+ Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index);
+ if (navpoly.is_valid()) {
+ PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices();
+
+ for (int i = 0; i < navpoly->get_polygon_count(); i++) {
+ // An array of vertices for this polygon.
+ Vector<int> polygon = navpoly->get_polygon(i);
+ Vector<Vector2> vertices;
+ vertices.resize(polygon.size());
+ for (int j = 0; j < polygon.size(); j++) {
+ ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
+ vertices.write[j] = navigation_polygon_vertices[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);
+
+ rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////// Scenes //////////////////////////////////////
+
+void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ // Clear the scenes.
+ for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) {
+ Node *node = get_node(E->get());
+ if (node) {
+ node->queue_delete();
+ }
+ }
+
+ q.scenes.clear();
+
+ // Recreate the scenes.
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = get_cell(q.layer, E_cell->get(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scenes_collection_source) {
+ Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile);
+ if (packed_scene.is_valid()) {
+ Node *scene = packed_scene->instantiate();
+ add_child(scene);
+ Control *scene_as_control = Object::cast_to<Control>(scene);
+ Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene);
+ if (scene_as_control) {
+ scene_as_control->set_position(map_to_world(E_cell->get()) + scene_as_control->get_position());
+ } else if (scene_as_node2d) {
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()));
+ scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform());
+ }
+ q.scenes[E_cell->get()] = scene->get_name();
+ }
+ }
+ }
+ }
+
+ q_list_element = q_list_element->next();
+ }
+}
+
+void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+ // Clear the scenes.
+ for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) {
+ Node *node = get_node(E->get());
+ if (node) {
+ node->queue_delete();
+ }
+ }
+
+ p_quadrant->scenes.clear();
+}
+
+void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!Engine::get_singleton()->is_editor_hint()) {
return;
}
- if (p_update) {
- call_deferred(SNAME("update_dirty_quadrants"));
+ // Draw a placeholder for scenes needing one.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
+ if (scenes_collection_source) {
+ if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) {
+ // Generate a random color from the hashed values of the tiles.
+ Array to_hash;
+ to_hash.push_back(c.source_id);
+ to_hash.push_back(c.alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw a placeholder tile.
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
+ }
+ }
+ }
}
}
-void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
// Set the current cell tile (using integer position).
+ Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
Vector2i pk(p_coords);
Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk);
@@ -526,9 +1555,9 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i
}
// Get the quadrant
- Vector2i qk = _coords_to_quadrant_coords(pk);
+ Vector2i qk = _coords_to_quadrant_coords(p_layer, pk);
- Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk);
+ Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk);
if (source_id == TileSet::INVALID_SOURCE) {
// Erase existing cell in the tile map.
@@ -555,7 +1584,7 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i
// Create a new quadrant if needed, then insert the cell if needed.
if (!Q) {
- Q = _create_quadrant(qk);
+ Q = _create_quadrant(p_layer, qk);
}
TileMapQuadrant &q = Q->get();
q.cells.insert(pk);
@@ -579,8 +1608,11 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i
}
}
-int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const {
+int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE);
+
// Get a cell source id from position
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
@@ -595,8 +1627,11 @@ int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) co
return E->get().source_id;
}
-Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const {
+Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS);
+
// Get a cell source id from position
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
@@ -611,8 +1646,11 @@ Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_pro
return E->get().get_atlas_coords();
}
-int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const {
+int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
+
// Get a cell source id from position
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
@@ -627,7 +1665,8 @@ int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_prox
return E->get().alternative_tile;
}
-TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) {
+TileMapPattern *TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
TileMapPattern *output = memnew(TileMapPattern);
@@ -673,7 +1712,7 @@ TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) {
for (int i = 0; i < coords_in_pattern_array.size(); i++) {
Vector2i coords = p_coords_array[i];
Vector2i coords_in_pattern = coords_in_pattern_array[i];
- output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords));
+ output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(p_layer, coords), get_cell_atlas_coords(p_layer, coords), get_cell_alternative_tile(p_layer, coords));
}
return output;
@@ -702,17 +1741,20 @@ Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_
return output;
}
-void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) {
+void TileMap::set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_COND(!tile_set.is_valid());
TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
for (int i = 0; i < used_cells.size(); i++) {
Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern);
- set_cell(coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords));
+ set_cell(p_layer, coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords));
}
}
-TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) const {
+TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell());
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
if (!tile_map.has(p_coords)) {
return TileMapCell();
} else {
@@ -727,77 +1769,62 @@ TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) cons
}
}
-Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() {
- return quadrant_map;
+Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
+
+ return &layers[p_layer].quadrant_map;
}
void TileMap::fix_invalid_tiles() {
ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
- Set<Vector2i> coords;
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- TileSetSource *source = *tile_set->get_source(E->get().source_id);
- if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
- coords.insert(E->key());
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
+ Set<Vector2i> coords;
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ TileSetSource *source = *tile_set->get_source(E->get().source_id);
+ if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
+ coords.insert(E->key());
+ }
}
- }
- for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
- set_cell(E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- }
-}
-
-void TileMap::_recreate_quadrants() {
- // Clear then recreate all quadrants.
- _clear_quadrants();
-
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- Vector2i qk = _coords_to_quadrant_coords(Vector2i(E->key().x, E->key().y));
-
- Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk);
- if (!Q) {
- Q = _create_quadrant(qk);
- dirty_quadrant_list.add(&Q->get().dirty_list_element);
+ for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
+ set_cell(i, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
}
-
- Vector2i pk = E->key();
- Q->get().cells.insert(pk);
-
- _make_quadrant_dirty(Q, false);
}
-
- update_dirty_quadrants();
}
-void TileMap::_clear_quadrants() {
- // Clear quadrants.
- while (quadrant_map.size()) {
- _erase_quadrant(quadrant_map.front());
- }
+void TileMap::clear_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
- // Clear the dirty quadrants list.
- while (dirty_quadrant_list.first()) {
- dirty_quadrant_list.remove(dirty_quadrant_list.first());
- }
+ // Remove all tiles.
+ _clear_layer_internals(p_layer);
+ layers[p_layer].tile_map.clear();
+
+ used_size_cache_dirty = true;
}
void TileMap::clear() {
// Remove all tiles.
- _clear_quadrants();
- tile_map.clear();
+ _clear_internals();
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ layers[i].tile_map.clear();
+ }
used_size_cache_dirty = true;
}
-void TileMap::_set_tile_data(const Vector<int> &p_data) {
- // Set data for a given tile from raw data.
+void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_COND(format > FORMAT_3);
+ // Set data for a given tile from raw data.
+
int c = p_data.size();
const int *r = p_data.ptr();
int offset = (format >= FORMAT_2) ? 3 : 2;
ERR_FAIL_COND_MSG(c % offset != 0, "Corrupted tile data.");
- clear();
+ clear_layer(p_layer);
#ifdef DISABLE_DEPRECATED
ERR_FAIL_COND_MSG(format != FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", format));
@@ -831,7 +1858,7 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) {
uint16_t atlas_coords_x = decode_uint16(&local[6]);
uint16_t atlas_coords_y = decode_uint16(&local[8]);
uint16_t alternative_tile = decode_uint16(&local[10]);
- set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
+ set_cell(p_layer, Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
} else {
#ifndef DISABLE_DEPRECATED
// Previous decated format.
@@ -854,21 +1881,25 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) {
if (tile_set.is_valid()) {
Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose);
if (a.size() == 3) {
- set_cell(Vector2i(x, y), a[0], a[1], a[2]);
+ set_cell(p_layer, Vector2i(x, y), a[0], a[1], a[2]);
} else {
ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose));
}
} else {
int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2);
- set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile);
+ set_cell(p_layer, Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile);
}
#endif
}
}
+ emit_signal(SNAME("changed"));
}
-Vector<int> TileMap::_get_tile_data() const {
+Vector<int> TileMap::_get_tile_data(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>());
+
// Export tile data to raw format
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
Vector<int> data;
data.resize(tile_map.size() * 3);
int *w = data.ptrw();
@@ -894,7 +1925,7 @@ Vector<int> TileMap::_get_tile_data() const {
Rect2 TileMap::_edit_get_rect() const {
// Return the visible rect of the tilemap
if (pending_update) {
- const_cast<TileMap *>(this)->update_dirty_quadrants();
+ const_cast<TileMap *>(this)->_update_dirty_quadrants();
} else {
const_cast<TileMap *>(this)->_recompute_rect_cache();
}
@@ -903,38 +1934,99 @@ Rect2 TileMap::_edit_get_rect() const {
#endif
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "format") {
if (p_value.get_type() == Variant::INT) {
format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading
return true;
}
- } else if (p_name == "tile_data") {
+ } else if (p_name == "tile_data") { // Kept for compatibility reasons.
if (p_value.is_array()) {
- _set_tile_data(p_value);
+ if (layers.size() < 1) {
+ layers.resize(1);
+ }
+ _set_tile_data(0, p_value);
return true;
}
return false;
+ } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
+ int index = components[0].trim_prefix("layer_").to_int();
+ if (index < 0 || index >= (int)layers.size()) {
+ return false;
+ }
+
+ if (components[1] == "name") {
+ set_layer_name(index, p_value);
+ return true;
+ } else if (components[1] == "enabled") {
+ set_layer_enabled(index, p_value);
+ return true;
+ } else if (components[1] == "y_sort_enabled") {
+ set_layer_y_sort_enabled(index, p_value);
+ return true;
+ } else if (components[1] == "y_sort_origin") {
+ set_layer_y_sort_origin(index, p_value);
+ return true;
+ } else if (components[1] == "z_index") {
+ set_layer_z_index(index, p_value);
+ return true;
+ } else if (components[1] == "tile_data") {
+ _set_tile_data(index, p_value);
+ return true;
+ } else {
+ return false;
+ }
}
return false;
}
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "format") {
r_ret = FORMAT_3; // When saving, always save highest format
return true;
- } else if (p_name == "tile_data") {
- r_ret = _get_tile_data();
- return true;
+ } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
+ int index = components[0].trim_prefix("layer_").to_int();
+ if (index < 0 || index >= (int)layers.size()) {
+ return false;
+ }
+
+ if (components[1] == "name") {
+ r_ret = get_layer_name(index);
+ return true;
+ } else if (components[1] == "enabled") {
+ r_ret = is_layer_enabled(index);
+ return true;
+ } else if (components[1] == "y_sort_enabled") {
+ r_ret = is_layer_y_sort_enabled(index);
+ return true;
+ } else if (components[1] == "y_sort_origin") {
+ r_ret = get_layer_y_sort_origin(index);
+ return true;
+ } else if (components[1] == "z_index") {
+ r_ret = get_layer_z_index(index);
+ return true;
+ } else if (components[1] == "tile_data") {
+ r_ret = _get_tile_data(index);
+ return true;
+ } else {
+ return false;
+ }
}
return false;
}
void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
- PropertyInfo p(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL);
- p_list->push_back(p);
-
- p = PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL);
- p_list->push_back(p);
+ p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
}
Vector2 TileMap::map_to_world(const Vector2i &p_pos) const {
@@ -1557,12 +2649,14 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh
}
}
-TypedArray<Vector2i> TileMap::get_used_cells() const {
+TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>());
+
// Returns the cells used in the tilemap.
TypedArray<Vector2i> a;
- a.resize(tile_map.size());
+ a.resize(layers[p_layer].tile_map.size());
int i = 0;
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) {
Vector2i p(E->key().x, E->key().y);
a[i++] = p;
}
@@ -1573,18 +2667,20 @@ TypedArray<Vector2i> TileMap::get_used_cells() const {
Rect2 TileMap::get_used_rect() { // Not const because of cache
// Return the rect of the currently used area
if (used_size_cache_dirty) {
- if (tile_map.size() > 0) {
- used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0);
+ bool first = true;
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
+ if (tile_map.size() > 0) {
+ if (first) {
+ used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 1, 1);
+ first = false;
+ }
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- used_size_cache.expand_to(Vector2(E->key().x, E->key().y));
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ used_size_cache.expand_to(Vector2(E->key().x + 1, E->key().y + 1));
+ }
}
-
- used_size_cache.size += Vector2(1, 1);
- } else {
- used_size_cache = Rect2();
}
-
used_size_cache_dirty = false;
}
@@ -1596,10 +2692,13 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache
void TileMap::set_light_mask(int p_light_mask) {
// Occlusion: set light mask.
CanvasItem::set_light_mask(p_light_mask);
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- for (const RID &F : E->get().canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_light_mask(F, get_light_mask());
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ for (const RID &ci : E->get().canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask());
+ }
}
+ _rendering_update_layer(layer);
}
}
@@ -1608,11 +2707,14 @@ void TileMap::set_material(const Ref<Material> &p_material) {
CanvasItem::set_material(p_material);
// Update material for the whole tilemap.
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
- for (const RID &F : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid());
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+ for (const RID &ci : q.canvas_items) {
+ RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
+ }
}
+ _rendering_update_layer(layer);
}
}
@@ -1621,35 +2723,44 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) {
CanvasItem::set_use_parent_material(p_use_parent_material);
// Update use_parent_material for the whole tilemap.
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
- for (const RID &F : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid());
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+ for (const RID &ci : q.canvas_items) {
+ RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
+ }
}
+ _rendering_update_layer(layer);
}
}
void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap
CanvasItem::set_texture_filter(p_texture_filter);
- for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
- TileMapQuadrant &q = F->get();
- for (const RID &E : q.canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E, RS::CanvasItemTextureFilter(p_texture_filter));
- _make_quadrant_dirty(F);
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) {
+ TileMapQuadrant &q = F->get();
+ for (const RID &ci : q.canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(p_texture_filter));
+ _make_quadrant_dirty(F);
+ }
}
+ _rendering_update_layer(layer);
}
}
void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
// Set a default texture repeat for the whole tilemap
CanvasItem::set_texture_repeat(p_texture_repeat);
- for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
- TileMapQuadrant &q = F->get();
- for (const RID &E : q.canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E, RS::CanvasItemTextureRepeat(p_texture_repeat));
- _make_quadrant_dirty(F);
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) {
+ TileMapQuadrant &q = F->get();
+ for (const RID &ci : q.canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(p_texture_repeat));
+ _make_quadrant_dirty(F);
+ }
}
+ _rendering_update_layer(layer);
}
}
@@ -1744,6 +2855,28 @@ void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Colo
}
}
+TypedArray<String> TileMap::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ // Retrieve the set of Z index values with a Y-sorted layer.
+ Set<int> y_sorted_z_index;
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ if (layers[layer].y_sort_enabled) {
+ y_sorted_z_index.insert(layers[layer].z_index);
+ }
+ }
+
+ // Check if we have a non-sorted layer in a Z-index with a Y-sorted layer.
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) {
+ warnings.push_back(TTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
+ break;
+ }
+ }
+
+ return warnings;
+}
+
void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset);
ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset);
@@ -1751,22 +2884,35 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "show_collision"), &TileMap::set_collision_visibility_mode);
+ ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count);
+ ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count);
+ ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name);
+ ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name);
+ ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled);
+ ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled);
+ ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled);
+ ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled);
+ ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index);
+ ClassDB::bind_method(D_METHOD("get_layer_z_indexd", "layer"), &TileMap::get_layer_z_index);
+
+ ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode);
ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode);
- ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &TileMap::set_navigation_visibility_mode);
+ ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode);
ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode);
- ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
- ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords", "use_proxies"), &TileMap::get_cell_source_id);
- ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords);
- ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile);
+ ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id);
+ ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords);
+ ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile);
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);
- ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells);
+ ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells);
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect);
ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world);
@@ -1774,15 +2920,19 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell);
- ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants);
+ ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants);
- ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data);
- ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data);
+ ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data);
+ ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "show_collision", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "show_navigation", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
+
+ ADD_GROUP("Layers", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count");
+ ADD_PROPERTY_DEFAULT("layers_count", 1);
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
@@ -1795,17 +2945,19 @@ void TileMap::_bind_methods() {
void TileMap::_tile_set_changed() {
emit_signal(SNAME("changed"));
- _make_all_quadrants_dirty(true);
+ _recreate_internals();
}
TileMap::TileMap() {
set_notify_transform(true);
set_notify_local_transform(false);
+
+ layers.resize(1);
}
TileMap::~TileMap() {
if (tile_set.is_valid()) {
tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
}
- _clear_quadrants();
+ _clear_internals();
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 9e35e73ad8..4800780f94 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -99,7 +99,8 @@ struct TileMapQuadrant {
// Dirty list element
SelfList<TileMapQuadrant> dirty_list_element;
- // Quadrant coords.
+ // Quadrant layer and coords.
+ int layer = -1;
Vector2i coords;
// TileMapCells
@@ -126,6 +127,7 @@ struct TileMapQuadrant {
Map<Vector2i, String> scenes;
void operator=(const TileMapQuadrant &q) {
+ layer = q.layer;
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
@@ -136,6 +138,7 @@ struct TileMapQuadrant {
TileMapQuadrant(const TileMapQuadrant &q) :
dirty_list_element(this) {
+ layer = q.layer;
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
@@ -196,11 +199,13 @@ private:
};
mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present;
+ static constexpr float FP_ADJUST = 0.00001;
+
// Properties.
Ref<TileSet> tile_set;
int quadrant_size = 16;
- VisibilityMode show_collision = VISIBILITY_MODE_DEFAULT;
- VisibilityMode show_navigation = VISIBILITY_MODE_DEFAULT;
+ VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
+ VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
// Updates.
bool pending_update = false;
@@ -211,25 +216,69 @@ private:
Rect2 used_size_cache;
bool used_size_cache_dirty = true;
- // Map of cells.
- Map<Vector2i, TileMapCell> tile_map;
+ // TileMap layers.
+ struct TileMapLayer {
+ String name;
+ bool enabled = true;
+ bool y_sort_enabled = false;
+ int y_sort_origin = 0;
+ int z_index = 0;
+ RID canvas_item;
+ Map<Vector2i, TileMapCell> tile_map;
+ Map<Vector2i, TileMapQuadrant> quadrant_map;
+ SelfList<TileMapQuadrant>::List dirty_quadrant_list;
+ };
+ LocalVector<TileMapLayer> layers;
+ int selected_layer = -1;
+
+ // Quadrants and internals management.
+ Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const;
+
+ Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(int p_layer, const Vector2i &p_qk);
+
+ void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q);
+ void _make_all_quadrants_dirty();
+ void _queue_update_dirty_quadrants();
+
+ void _update_dirty_quadrants();
- // Quadrants management.
- Map<Vector2i, TileMapQuadrant> quadrant_map;
- Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
- SelfList<TileMapQuadrant>::List dirty_quadrant_list;
+ void _recreate_internals();
- Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk);
void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q);
- void _make_all_quadrants_dirty(bool p_update = true);
- void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update = true);
- void _recreate_quadrants();
- void _clear_quadrants();
+ void _clear_layer_internals(int p_layer);
+ void _clear_internals();
+
+ // Rect caching.
void _recompute_rect_cache();
+ // Per-system methods.
+ bool _rendering_quadrant_order_dirty = false;
+ void _rendering_notification(int p_what);
+ void _rendering_update_layer(int p_layer);
+ void _rendering_cleanup_layer(int p_layer);
+ void _rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _rendering_create_quadrant(TileMapQuadrant *p_quadrant);
+ void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+
+ void _physics_notification(int p_what);
+ void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _physics_create_quadrant(TileMapQuadrant *p_quadrant);
+ void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+
+ void _navigation_notification(int p_what);
+ void _navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+
+ void _scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+
// Set and get tiles from data arrays.
- void _set_tile_data(const Vector<int> &p_data);
- Vector<int> _get_tile_data() const;
+ void _set_tile_data(int p_layer, const Vector<int> &p_data);
+ Vector<int> _get_tile_data(int p_layer) const;
void _tile_set_changed();
@@ -258,27 +307,45 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
+ 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));
+
+ // Layers management.
+ void set_layers_count(int p_layers_count);
+ int get_layers_count() const;
+ void set_layer_name(int p_layer, String p_name);
+ String get_layer_name(int p_layer) const;
+ void set_layer_enabled(int p_layer, bool p_visible);
+ bool is_layer_enabled(int p_layer) const;
+ void set_layer_y_sort_enabled(int p_layer, bool p_enabled);
+ bool is_layer_y_sort_enabled(int p_layer) const;
+ void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin);
+ int get_layer_y_sort_origin(int p_layer) const;
+ void set_layer_z_index(int p_layer, int p_z_index);
+ int get_layer_z_index(int p_layer) const;
+ void set_selected_layer(int p_layer_id); // For editor use.
+ int get_selected_layer() const;
+
void set_collision_visibility_mode(VisibilityMode p_show_collision);
VisibilityMode get_collision_visibility_mode();
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
VisibilityMode get_navigation_visibility_mode();
- void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
- int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const;
- Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const;
- int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const;
+ void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
+ int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
- TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array);
+ TileMapPattern *get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern);
- void set_pattern(Vector2i p_position, const TileMapPattern *p_pattern);
+ void set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern);
// Not exposed to users
- TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
- Map<Vector2i, TileMapQuadrant> &get_quadrant_map();
- int get_effective_quadrant_size() const;
+ TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);
+ int get_effective_quadrant_size(int p_layer) const;
+ //---
- void update_dirty_quadrants();
virtual void set_y_sort_enabled(bool p_enable) override;
Vector2 map_to_world(const Vector2i &p_pos) const;
@@ -287,7 +354,7 @@ public:
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
- TypedArray<Vector2i> get_used_cells() const;
+ TypedArray<Vector2i> get_used_cells(int p_layer) const;
Rect2 get_used_rect(); // Not const because of cache
// Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems
@@ -297,13 +364,19 @@ public:
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
+ // Fixing a nclearing methods.
void fix_invalid_tiles();
+
+ void clear_layer(int p_layer);
void clear();
// Helpers
TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords);
void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D());
+ // Configuration warnings.
+ TypedArray<String> get_configuration_warnings() const override;
+
TileMap();
~TileMap();
};
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 10e6642303..be711b4c4f 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -462,8 +462,6 @@ private:
void _gui_input(Ref<InputEvent> p_event);
void _notification(int p_what);
- Size2 get_minimum_size() const override;
-
void item_edited(int p_column, TreeItem *p_item, bool p_lmb = true);
void item_changed(int p_column, TreeItem *p_item);
void item_selected(int p_column, TreeItem *p_item);
@@ -721,6 +719,8 @@ public:
void set_allow_reselect(bool p_allow);
bool get_allow_reselect() const;
+ Size2 get_minimum_size() const override;
+
Tree();
~Tree();
};
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 737b47ed95..8206385682 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -62,11 +62,6 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
"top_right_corner"
};
-// --- Plugins ---
-Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const {
- return tile_set_plugins_vector;
-}
-
// -- Shape and layout --
void TileSet::set_tile_shape(TileSet::TileShape p_shape) {
tile_shape = p_shape;
@@ -205,21 +200,11 @@ void TileSet::set_uv_clipping(bool p_uv_clipping) {
uv_clipping = p_uv_clipping;
emit_changed();
}
+
bool TileSet::is_uv_clipping() const {
return uv_clipping;
};
-void TileSet::set_y_sorting(bool p_y_sort) {
- if (y_sorting == p_y_sort) {
- return;
- }
- y_sorting = p_y_sort;
- emit_changed();
-}
-bool TileSet::is_y_sorting() const {
- return y_sorting;
-};
-
void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
ERR_FAIL_COND(p_occlusion_layers_count < 0);
if (occlusion_layers.size() == p_occlusion_layers_count) {
@@ -2604,8 +2589,6 @@ void TileSet::_bind_methods() {
// Rendering.
ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
- ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting);
- ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting);
ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
@@ -2670,7 +2653,6 @@ void TileSet::_bind_methods() {
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
ADD_GROUP("Physics", "");
@@ -2727,12 +2709,6 @@ TileSet::TileSet() {
// Instantiate the tile meshes.
tile_lines_mesh.instantiate();
tile_filled_mesh.instantiate();
-
- // Instantiate and list all plugins.
- tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering));
- tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics));
- tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation));
- tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections));
}
TileSet::~TileSet() {
@@ -2744,9 +2720,6 @@ TileSet::~TileSet() {
while (!source_ids.is_empty()) {
remove_source(source_ids[0]);
}
- for (int i = 0; i < tile_set_plugins_vector.size(); i++) {
- memdelete(tile_set_plugins_vector[i]);
- }
}
/////////////////////////////// TileSetSource //////////////////////////////////////
@@ -2972,23 +2945,22 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
// Get the alternative tile's properties and append them to the list of properties.
List<PropertyInfo> alternative_property_list;
E_alternative->get()->get_property_list(&alternative_property_list);
- for (List<PropertyInfo>::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) {
- property_info = E_property->get();
+ for (PropertyInfo &alternative_property_info : alternative_property_list) {
bool valid;
- Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid);
- Variant value = E_alternative->get()->get(property_info.name);
+ Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid);
+ Variant value = E_alternative->get()->get(alternative_property_info.name);
if (valid && value == default_value) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
- property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name);
- tile_property_list.push_back(property_info);
+ alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name);
+ tile_property_list.push_back(alternative_property_info);
}
}
// Add all alternative.
- for (List<PropertyInfo>::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) {
- E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name);
- p_list->push_back(E_property->get());
+ for (PropertyInfo &tile_property_info : tile_property_list) {
+ tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name);
+ p_list->push_back(tile_property_info);
}
}
}
@@ -4238,842 +4210,3 @@ void TileData::_bind_methods() {
ADD_SIGNAL(MethodInfo("changed"));
}
-/////////////////////////////// TileSetPluginAtlasRendering //////////////////////////////////////
-
-void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) {
- switch (p_what) {
- case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
- bool visible = p_tile_map->is_visible_in_tree();
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
-
- // Update occluders transform.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
- Transform2D xform;
- xform.set_origin(E_cell->key());
- for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) {
- RS::get_singleton()->canvas_light_occluder_set_enabled(E_occluder_id->get(), visible);
- }
- }
- }
- } break;
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
- if (!p_tile_map->is_inside_tree()) {
- return;
- }
-
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
-
- // Update occluders transform.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
- Transform2D xform;
- xform.set_origin(E_cell->key());
- for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) {
- RS::get_singleton()->canvas_light_occluder_set_transform(E_occluder_id->get(), p_tile_map->get_global_transform() * xform);
- }
- }
- }
- } break;
- case CanvasItem::NOTIFICATION_DRAW: {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- if (tile_set.is_valid() || p_tile_map->is_y_sort_enabled()) {
- RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(p_tile_map->get_canvas_item(), tile_set->is_y_sorting() || p_tile_map->is_y_sort_enabled());
- }
- } break;
- }
-}
-
-void TileSetPluginAtlasRendering::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) {
- ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
-
- TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- // Get the texture.
- Ref<Texture2D> tex = atlas_source->get_texture();
- if (!tex.is_valid()) {
- return;
- }
-
- // Check if we are in the texture, return otherwise.
- Vector2i grid_size = atlas_source->get_atlas_grid_size();
- if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
- return;
- }
-
- // Get tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
-
- // Compute the offset
- Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords);
- Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile);
-
- // Compute the destination rectangle in the CanvasItem.
- Rect2 dest_rect;
- dest_rect.size = source_rect.size;
- dest_rect.size.x += fp_adjust;
- dest_rect.size.y += fp_adjust;
-
- bool transpose = tile_data->get_transpose();
- if (transpose) {
- dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
- } else {
- dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
- }
-
- if (tile_data->get_flip_h()) {
- dest_rect.size.x = -dest_rect.size.x;
- }
-
- if (tile_data->get_flip_v()) {
- dest_rect.size.y = -dest_rect.size.y;
- }
-
- // Get the tile modulation.
- Color modulate = tile_data->get_modulate();
- modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a);
-
- // Draw the tile.
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
- }
-}
-
-void TileSetPluginAtlasRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!p_tile_map);
- ERR_FAIL_COND(!p_tile_map->is_inside_tree());
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- bool visible = p_tile_map->is_visible_in_tree();
-
- SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
- while (q_list_element) {
- TileMapQuadrant &q = *q_list_element->self();
-
- RenderingServer *rs = RenderingServer::get_singleton();
-
- // Free the canvas items.
- for (const RID &E : q.canvas_items) {
- rs->free(E);
- }
- q.canvas_items.clear();
-
- // Free the occluders.
- for (const RID &E : q.occluders) {
- rs->free(E);
- }
- q.occluders.clear();
-
- // Those allow to group cell per material or z-index.
- Ref<ShaderMaterial> prev_material;
- int prev_z_index = 0;
- RID prev_canvas_item;
-
- // Iterate over the cells of the quadrant.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = p_tile_map->get_cell(E_cell->value(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- // Get the tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
- Ref<ShaderMaterial> mat = tile_data->tile_get_material();
- int z_index = tile_data->get_z_index();
-
- // Quandrant pos.
- Vector2 position = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size());
- if (tile_set->is_y_sorting()) {
- // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem.
- position.y += tile_data->get_y_sort_origin();
- }
-
- // --- CanvasItems ---
- // Create two canvas items, for rendering and debug.
- RID canvas_item;
-
- // Check if the material or the z_index changed.
- if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
- // If so, create a new CanvasItem.
- canvas_item = rs->canvas_item_create();
- if (mat.is_valid()) {
- rs->canvas_item_set_material(canvas_item, mat->get_rid());
- }
- rs->canvas_item_set_parent(canvas_item, p_tile_map->get_canvas_item());
- rs->canvas_item_set_use_parent_material(canvas_item, p_tile_map->get_use_parent_material() || p_tile_map->get_material().is_valid());
-
- Transform2D xform;
- xform.set_origin(position);
- rs->canvas_item_set_transform(canvas_item, xform);
-
- rs->canvas_item_set_light_mask(canvas_item, p_tile_map->get_light_mask());
- rs->canvas_item_set_z_index(canvas_item, z_index);
-
- rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(p_tile_map->CanvasItem::get_texture_filter()));
- rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(p_tile_map->CanvasItem::get_texture_repeat()));
-
- q.canvas_items.push_back(canvas_item);
-
- prev_canvas_item = canvas_item;
- prev_material = mat;
- prev_z_index = z_index;
-
- } else {
- // Keep the same canvas_item to draw on.
- canvas_item = prev_canvas_item;
- }
-
- // Drawing the tile in the canvas item.
- draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, p_tile_map->get_self_modulate());
-
- // --- Occluders ---
- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
- Transform2D xform;
- xform.set_origin(E_cell->key());
- if (tile_data->get_occluder(i).is_valid()) {
- RID occluder_id = rs->canvas_light_occluder_create();
- rs->canvas_light_occluder_set_enabled(occluder_id, visible);
- rs->canvas_light_occluder_set_transform(occluder_id, p_tile_map->get_global_transform() * xform);
- rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid());
- rs->canvas_light_occluder_attach_to_canvas(occluder_id, p_tile_map->get_canvas());
- rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
- q.occluders.push_back(occluder_id);
- }
- }
- }
- }
- }
-
- quadrant_order_dirty = true;
- q_list_element = q_list_element->next();
- }
-
- // Reset the drawing indices
- if (quadrant_order_dirty) {
- int index = -(int64_t)0x80000000; //always must be drawn below children.
-
- // Sort the quadrants coords per world coordinates
- Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
- Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- world_to_map[p_tile_map->map_to_world(E->key())] = E->key();
- }
-
- // Sort the quadrants
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = quadrant_map[E->value()];
- for (const RID &F : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_draw_index(F, index++);
- }
- }
-
- quadrant_order_dirty = false;
- }
-}
-
-void TileSetPluginAtlasRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- quadrant_order_dirty = true;
-}
-
-void TileSetPluginAtlasRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Free the canvas items.
- for (const RID &E : p_quadrant->canvas_items) {
- RenderingServer::get_singleton()->free(E);
- }
- p_quadrant->canvas_items.clear();
-
- // Free the occluders.
- for (const RID &E : p_quadrant->occluders) {
- RenderingServer::get_singleton()->free(E);
- }
- p_quadrant->occluders.clear();
-}
-
-void TileSetPluginAtlasRendering::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- if (!Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-
- // Draw a placeholder for scenes needing one.
- RenderingServer *rs = RenderingServer::get_singleton();
- Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
- const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- Vector2i grid_size = atlas_source->get_atlas_grid_size();
- if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
- // Generate a random color from the hashed values of the tiles.
- Array to_hash;
- to_hash.push_back(c.source_id);
- to_hash.push_back(c.get_atlas_coords());
- to_hash.push_back(c.alternative_tile);
- uint32_t hash = RandomPCG(to_hash.hash()).rand();
-
- Color color;
- color = color.from_hsv(
- (float)((hash >> 24) & 0xFF) / 256.0,
- Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
- Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
- 0.8);
-
- // Draw a placeholder tile.
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
- rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
- }
- }
- }
- }
-}
-
-/////////////////////////////// TileSetPluginAtlasPhysics //////////////////////////////////////
-
-void TileSetPluginAtlasPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) {
- switch (p_what) {
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
- // Update the bodies transforms.
- if (p_tile_map->is_inside_tree()) {
- Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
- Transform2D global_transform = p_tile_map->get_global_transform();
-
- for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
-
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E->key() * p_tile_map->get_effective_quadrant_size()));
- xform = global_transform * xform;
-
- for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
- PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
- }
- }
- } break;
- }
-}
-
-void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!p_tile_map);
- ERR_FAIL_COND(!p_tile_map->is_inside_tree());
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- Transform2D global_transform = p_tile_map->get_global_transform();
- PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
-
- SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
- while (q_list_element) {
- TileMapQuadrant &q = *q_list_element->self();
-
- Vector2 quadrant_pos = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size());
-
- // Clear shapes.
- for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
- ps->body_clear_shapes(q.bodies[body_index]);
-
- // Position the bodies.
- Transform2D xform;
- xform.set_origin(quadrant_pos);
- xform = global_transform * xform;
- ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
-
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- 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 < q.bodies.size(); body_index++) {
- // Add the shapes again.
- 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);
- }
- }
- }
- }
- }
- }
-
- q_list_element = q_list_element->next();
- }
-}
-
-void TileSetPluginAtlasPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- //Get the TileMap's gobla transform.
- Transform2D global_transform;
- if (p_tile_map->is_inside_tree()) {
- global_transform = p_tile_map->get_global_transform();
- }
-
- // Clear all bodies.
- p_quadrant->bodies.clear();
-
- // Create the body and set its parameters.
- for (int layer_index = 0; layer_index < tile_set->get_physics_layers_count(); layer_index++) {
- RID body = PhysicsServer2D::get_singleton()->body_create();
- PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC);
-
- PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, p_tile_map->get_instance_id());
- PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer_index));
- PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer_index));
-
- Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer_index);
- if (!physics_material.is_valid()) {
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
- } else {
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
- }
-
- if (p_tile_map->is_inside_tree()) {
- RID space = p_tile_map->get_world_2d()->get_space();
- PhysicsServer2D::get_singleton()->body_set_space(body, space);
-
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()));
- xform = global_transform * xform;
- PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
-
- p_quadrant->bodies.push_back(body);
- }
-}
-
-void TileSetPluginAtlasPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Remove a quadrant.
- for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
- PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
- }
- p_quadrant->bodies.clear();
-}
-
-void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Draw the debug collision shapes.
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- if (!p_tile_map->get_tree()) {
- return;
- }
-
- bool show_collision = false;
- switch (p_tile_map->get_collision_visibility_mode()) {
- case TileMap::VISIBILITY_MODE_DEFAULT:
- show_collision = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint());
- break;
- case TileMap::VISIBILITY_MODE_FORCE_HIDE:
- show_collision = false;
- break;
- case TileMap::VISIBILITY_MODE_FORCE_SHOW:
- show_collision = true;
- break;
- }
- if (!show_collision) {
- return;
- }
-
- RenderingServer *rs = RenderingServer::get_singleton();
-
- Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
-
- Color debug_collision_color = p_tile_map->get_tree()->get_debug_collisions_color();
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = p_tile_map->get_cell(E_cell->get(), true);
-
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
-
- if (tile_set->has_source(c.source_id)) {
- TileSetSource *source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- 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 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);
- }
- }
- }
- }
- }
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D());
- }
-};
-
-/////////////////////////////// TileSetPluginAtlasNavigation //////////////////////////////////////
-
-void TileSetPluginAtlasNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) {
- switch (p_what) {
- case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
- if (p_tile_map->is_inside_tree()) {
- Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
- Transform2D tilemap_xform = p_tile_map->get_global_transform();
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
- for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) {
- for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) {
- RID region = E_region->get()[layer_index];
- if (!region.is_valid()) {
- continue;
- }
- Transform2D tile_transform;
- tile_transform.set_origin(p_tile_map->map_to_world(E_region->key()));
- NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
- }
- }
- }
- }
- } break;
- }
-}
-
-void TileSetPluginAtlasNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!p_tile_map);
- ERR_FAIL_COND(!p_tile_map->is_inside_tree());
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- // Get colors for debug.
- SceneTree *st = SceneTree::get_singleton();
- Color debug_navigation_color;
- bool debug_navigation = st && st->is_debugging_navigation_hint();
- if (debug_navigation) {
- debug_navigation_color = st->get_debug_navigation_color();
- }
-
- Transform2D tilemap_xform = p_tile_map->get_global_transform();
- SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
- while (q_list_element) {
- TileMapQuadrant &q = *q_list_element->self();
-
- // Clear navigation shapes in the quadrant.
- for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().size(); i++) {
- RID region = E->get()[i];
- if (!region.is_valid()) {
- continue;
- }
- NavigationServer2D::get_singleton()->region_set_map(region, RID());
- }
- }
- q.navigation_regions.clear();
-
- // Get the navigation polygons and create regions.
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
- q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
-
- for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
- Ref<NavigationPolygon> navpoly;
- navpoly = tile_data->get_navigation_polygon(layer_index);
-
- if (navpoly.is_valid()) {
- Transform2D tile_transform;
- tile_transform.set_origin(p_tile_map->map_to_world(E_cell->get()));
-
- RID region = NavigationServer2D::get_singleton()->region_create();
- NavigationServer2D::get_singleton()->region_set_map(region, p_tile_map->get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
- NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly);
- q.navigation_regions[E_cell->get()].write[layer_index] = region;
- }
- }
- }
- }
- }
-
- q_list_element = q_list_element->next();
- }
-}
-
-void TileSetPluginAtlasNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Clear navigation shapes in the quadrant.
- for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().size(); i++) {
- RID region = E->get()[i];
- if (!region.is_valid()) {
- continue;
- }
- NavigationServer2D::get_singleton()->free(region);
- }
- }
- p_quadrant->navigation_regions.clear();
-}
-
-void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Draw the debug collision shapes.
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- if (!p_tile_map->get_tree()) {
- return;
- }
-
- bool show_navigation = false;
- switch (p_tile_map->get_navigation_visibility_mode()) {
- case TileMap::VISIBILITY_MODE_DEFAULT:
- show_navigation = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint());
- break;
- case TileMap::VISIBILITY_MODE_FORCE_HIDE:
- show_navigation = false;
- break;
- case TileMap::VISIBILITY_MODE_FORCE_SHOW:
- show_navigation = true;
- break;
- }
- if (!show_navigation) {
- return;
- }
-
- RenderingServer *rs = RenderingServer::get_singleton();
-
- Color color = p_tile_map->get_tree()->get_debug_navigation_color();
- RandomPCG rand;
-
- Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
-
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
-
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
-
- for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
- Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index);
- if (navpoly.is_valid()) {
- PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices();
-
- for (int i = 0; i < navpoly->get_polygon_count(); i++) {
- // An array of vertices for this polygon.
- Vector<int> polygon = navpoly->get_polygon(i);
- Vector<Vector2> vertices;
- vertices.resize(polygon.size());
- for (int j = 0; j < polygon.size(); j++) {
- ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
- vertices.write[j] = navigation_polygon_vertices[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);
-
- rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors);
- }
- }
- }
- }
- }
- }
-}
-
-/////////////////////////////// TileSetPluginScenesCollections //////////////////////////////////////
-
-void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
- while (q_list_element) {
- TileMapQuadrant &q = *q_list_element->self();
-
- // Clear the scenes.
- for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) {
- Node *node = p_tile_map->get_node(E->get());
- if (node) {
- node->queue_delete();
- }
- }
-
- q.scenes.clear();
-
- // Recreate the scenes.
- for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
- const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
- if (scenes_collection_source) {
- Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile);
- if (packed_scene.is_valid()) {
- Node *scene = packed_scene->instantiate();
- p_tile_map->add_child(scene);
- Control *scene_as_control = Object::cast_to<Control>(scene);
- Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene);
- if (scene_as_control) {
- scene_as_control->set_position(p_tile_map->map_to_world(E_cell->get()) + scene_as_control->get_position());
- } else if (scene_as_node2d) {
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E_cell->get()));
- scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform());
- }
- q.scenes[E_cell->get()] = scene->get_name();
- }
- }
- }
- }
-
- q_list_element = q_list_element->next();
- }
-}
-
-void TileSetPluginScenesCollections::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- // Clear the scenes.
- for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) {
- Node *node = p_tile_map->get_node(E->get());
- if (node) {
- node->queue_delete();
- }
- }
-
- p_quadrant->scenes.clear();
-}
-
-void TileSetPluginScenesCollections::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
- Ref<TileSet> tile_set = p_tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- if (!Engine::get_singleton()->is_editor_hint()) {
- return;
- }
-
- // Draw a placeholder for scenes needing one.
- RenderingServer *rs = RenderingServer::get_singleton();
- Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
- const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true);
-
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
- if (scenes_collection_source) {
- if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) {
- // Generate a random color from the hashed values of the tiles.
- Array to_hash;
- to_hash.push_back(c.source_id);
- to_hash.push_back(c.alternative_tile);
- uint32_t hash = RandomPCG(to_hash.hash()).rand();
-
- Color color;
- color = color.from_hsv(
- (float)((hash >> 24) & 0xFF) / 256.0,
- Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
- Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
- 0.8);
-
- // Draw a placeholder tile.
- Transform2D xform;
- xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
- rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
- }
- }
- }
- }
-}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 0a07981171..35e6999d13 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -191,7 +191,6 @@ private:
Vector2 tile_skew = Vector2(0, 0);
// Rendering.
- bool y_sorting = false;
bool uv_clipping = false;
struct OcclusionLayer {
uint32_t light_mask = 1;
@@ -245,9 +244,6 @@ private:
int next_source_id = 0;
// ---------------------
- // Plugins themselves.
- Vector<TileSetPlugin *> tile_set_plugins_vector;
-
void _compute_next_source_id();
void _source_changed();
@@ -299,9 +295,6 @@ public:
Ref<TileSetSource> get_source(int p_source_id) const;
// Rendering
- void set_y_sorting(bool p_y_sort);
- bool is_y_sorting() const;
-
void set_uv_clipping(bool p_uv_clipping);
bool is_uv_clipping() const;
@@ -666,73 +659,6 @@ public:
Variant get_custom_data_by_layer_id(int p_layer_id) const;
};
-#include "scene/2d/tile_map.h"
-
-class TileSetPlugin : public Object {
- GDCLASS(TileSetPlugin, Object);
-
-public:
- // Tilemap updates.
- virtual void tilemap_notification(TileMap *p_tile_map, int p_what){};
- virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list){};
- virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
- virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
-
- virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
-};
-
-class TileSetPluginAtlasRendering : public TileSetPlugin {
- GDCLASS(TileSetPluginAtlasRendering, TileSetPlugin);
-
-private:
- static constexpr float fp_adjust = 0.00001;
- bool quadrant_order_dirty = false;
-
-public:
- // Tilemap updates
- virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
- virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
- virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
-
- // Other.
- 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 TileSetPluginAtlasPhysics : public TileSetPlugin {
- GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin);
-
-public:
- // Tilemap updates
- virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
- virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
- virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
-};
-
-class TileSetPluginAtlasNavigation : public TileSetPlugin {
- GDCLASS(TileSetPluginAtlasNavigation, TileSetPlugin);
-
-public:
- // Tilemap updates
- virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
- virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
- virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
-};
-
-class TileSetPluginScenesCollections : public TileSetPlugin {
- GDCLASS(TileSetPluginScenesCollections, TileSetPlugin);
-
-public:
- // Tilemap updates
- virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
- virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
- virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
-};
-
VARIANT_ENUM_CAST(TileSet::CellNeighbor);
VARIANT_ENUM_CAST(TileSet::TerrainMode);
VARIANT_ENUM_CAST(TileSet::TileShape);