Implements TileMap layers and move TileSetPlugins's functions to the TileMap node instead

This commit is contained in:
Gilles Roudière 2021-07-28 18:10:01 +02:00
parent 854725f400
commit ad8b5cd5a4
13 changed files with 2123 additions and 1465 deletions

View file

@ -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="show_navigation" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0">
<member name="layers_count" type="int" setter="set_layers_count" getter="get_layers_count" default="1">
</member>
<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].

View file

@ -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">

View file

@ -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>

After

Width:  |  Height:  |  Size: 635 B

View file

@ -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;

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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();

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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 management.
Map<Vector2i, TileMapQuadrant> quadrant_map;
Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
SelfList<TileMapQuadrant>::List dirty_quadrant_list;
// 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();
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();
};

View file

@ -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();
};

View file

@ -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);
}
}
}
}
}

View file

@ -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);