Rework the TileSet resource and TileMap nodes:

- Move most properties from TileMap to TileSet,
- Make TileSet more flexible, supporting more feature (several
  collision layers, etc...),
- Fusion both the TileMap and TileSet editor,
- Implement TileSetSources, and thus a new way to index tiles in the TileSet,
- Rework the TileSet and TileMap editors completely,
- Implement an editor zoom widget (and use it in several places)
This commit is contained in:
Gilles Roudière 2021-05-07 15:41:39 +02:00
parent d81ea631d9
commit a3dda2df85
48 changed files with 15216 additions and 10546 deletions

View file

@ -395,6 +395,45 @@ public:
H.resize(k);
return H;
}
static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end) {
Vector<Point2i> points;
Vector2i delta = (p_end - p_start).abs() * 2;
Vector2i step = (p_end - p_start).sign();
Vector2i current = p_start;
if (delta.x > delta.y) {
int err = delta.x / 2;
for (; current.x != p_end.x; current.x += step.x) {
points.push_back(current);
err -= delta.y;
if (err < 0) {
current.y += step.y;
err += delta.x;
}
}
} else {
int err = delta.y / 2;
for (; current.y != p_end.y; current.y += step.y) {
points.push_back(current);
err -= delta.x;
if (err < 0) {
current.x += step.x;
err += delta.y;
}
}
}
points.push_back(current);
return points;
}
static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon);
static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);

View file

@ -280,6 +280,14 @@ struct Vector2i {
return p_idx ? y : x;
}
Vector2i min(const Vector2i &p_vector2i) const {
return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
}
Vector2i max(const Vector2i &p_vector2i) const {
return Vector2(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
}
Vector2i operator+(const Vector2i &p_v) const;
void operator+=(const Vector2i &p_v);
Vector2i operator-(const Vector2i &p_v) const;

245
doc/classes/TileData.xml Normal file
View file

@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TileData" inherits="Object" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_collision_shape">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<description>
</description>
</method>
<method name="get_collision_shape_one_way_margin" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<description>
</description>
</method>
<method name="get_collision_shape_shape" qualifiers="const">
<return type="Shape2D">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<description>
</description>
</method>
<method name="get_collision_shapes_count" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<description>
</description>
</method>
<method name="get_custom_data" qualifiers="const">
<return type="Variant">
</return>
<argument index="0" name="layer_name" type="String">
</argument>
<description>
</description>
</method>
<method name="get_custom_data_by_layer_id" qualifiers="const">
<return type="Variant">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<description>
</description>
</method>
<method name="get_navigation_polygon" qualifiers="const">
<return type="NavigationPolygon">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<description>
</description>
</method>
<method name="get_occluder" qualifiers="const">
<return type="OccluderPolygon2D">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<description>
</description>
</method>
<method name="get_peering_bit_terrain" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor">
</argument>
<description>
</description>
</method>
<method name="is_collision_shape_one_way" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<description>
</description>
</method>
<method name="remove_collision_shape">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<description>
</description>
</method>
<method name="set_collision_shape_one_way">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<argument index="2" name="one_way" type="bool">
</argument>
<description>
</description>
</method>
<method name="set_collision_shape_one_way_margin">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<argument index="2" name="one_way_margin" type="float">
</argument>
<description>
</description>
</method>
<method name="set_collision_shape_shape">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shape_index" type="int">
</argument>
<argument index="2" name="shape" type="Shape2D">
</argument>
<description>
</description>
</method>
<method name="set_collision_shapes_count">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="shapes_count" type="int">
</argument>
<description>
</description>
</method>
<method name="set_custom_data">
<return type="void">
</return>
<argument index="0" name="layer_name" type="String">
</argument>
<argument index="1" name="value" type="Variant">
</argument>
<description>
</description>
</method>
<method name="set_custom_data_by_layer_id">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="value" type="Variant">
</argument>
<description>
</description>
</method>
<method name="set_navigation_polygon">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="navigation_polygon" type="NavigationPolygon">
</argument>
<description>
</description>
</method>
<method name="set_occluder">
<return type="void">
</return>
<argument index="0" name="layer_id" type="int">
</argument>
<argument index="1" name="occluder_polygon" type="OccluderPolygon2D">
</argument>
<description>
</description>
</method>
<method name="set_peering_bit_terrain">
<return type="void">
</return>
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor">
</argument>
<argument index="1" name="terrain" type="int">
</argument>
<description>
</description>
</method>
<method name="tile_get_material" qualifiers="const">
<return type="ShaderMaterial">
</return>
<description>
</description>
</method>
<method name="tile_set_material">
<return type="void">
</return>
<argument index="0" name="material" type="ShaderMaterial">
</argument>
<description>
</description>
</method>
</methods>
<members>
<member name="flip_h" type="bool" setter="set_flip_h" getter="get_flip_h" default="false">
</member>
<member name="flip_v" type="bool" setter="set_flip_v" getter="get_flip_v" default="false">
</member>
<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color( 1, 1, 1, 1 )">
</member>
<member name="probability" type="float" setter="set_probability" getter="get_probability" default="1.0">
</member>
<member name="terrain_set" type="int" setter="set_terrain_set" getter="get_terrain_set" default="-1">
</member>
<member name="texture_offset" type="Vector2i" setter="set_texture_offset" getter="get_texture_offset" default="Vector2i( 0, 0 )">
</member>
<member name="transpose" type="bool" setter="set_transpose" getter="get_transpose" default="false">
</member>
<member name="y_sort_origin" type="Vector2i" setter="set_y_sort_origin" getter="get_y_sort_origin" default="Vector2i( 0, 0 )">
</member>
<member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0">
</member>
</members>
<signals>
<signal name="changed">
<description>
</description>
</signal>
</signals>
<constants>
</constants>
</class>

View file

@ -31,53 +31,46 @@
Clears cells that do not exist in the tileset.
</description>
</method>
<method name="get_cell" qualifiers="const">
<method name="get_cell_alternative_tile" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="x" type="int">
</argument>
<argument index="1" name="y" type="int">
<argument index="0" name="coords" type="Vector2i">
</argument>
<description>
Returns the tile index of the given cell. If no tile exists in the cell, returns [constant INVALID_CELL].
</description>
</method>
<method name="get_cell_autotile_coord" qualifiers="const">
<return type="Vector2">
<method name="get_cell_atlas_coords" qualifiers="const">
<return type="Vector2i">
</return>
<argument index="0" name="x" type="int">
</argument>
<argument index="1" name="y" type="int">
<argument index="0" name="coords" type="Vector2i">
</argument>
<description>
Returns the coordinate (subtile column and row) of the autotile variation in the tileset. Returns a zero vector if the cell doesn't have autotiling.
</description>
</method>
<method name="get_cellv" qualifiers="const">
<method name="get_cell_source_id" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="position" type="Vector2">
<argument index="0" name="coords" type="Vector2i">
</argument>
<description>
Returns the tile index of the cell given by a Vector2. If no tile exists in the cell, returns [constant INVALID_CELL].
</description>
</method>
<method name="get_collision_layer_bit" qualifiers="const">
<return type="bool">
<method name="get_neighbor_cell" qualifiers="const">
<return type="Vector2i">
</return>
<argument index="0" name="bit" type="int">
<argument index="0" name="coords" type="Vector2i">
</argument>
<argument index="1" name="neighbor" type="int" enum="TileSet.CellNeighbor">
</argument>
<description>
Returns [code]true[/code] if the given collision layer bit is set.
</description>
</method>
<method name="get_collision_mask_bit" qualifiers="const">
<return type="bool">
<method name="get_surrounding_tiles">
<return type="Vector2i[]">
</return>
<argument index="0" name="bit" type="int">
<argument index="0" name="coords" type="Vector2i">
</argument>
<description>
Returns [code]true[/code] if the given collision mask bit is set.
</description>
</method>
<method name="get_used_cells" qualifiers="const">
@ -87,15 +80,6 @@
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>
</method>
<method name="get_used_cells_by_index" qualifiers="const">
<return type="Vector2i[]">
</return>
<argument index="0" name="index" type="int">
</argument>
<description>
Returns an array of all cells with the given tile [code]index[/code].
</description>
</method>
<method name="get_used_rect">
<return type="Rect2">
</return>
@ -103,155 +87,26 @@
Returns a rectangle enclosing the used (non-empty) tiles of the map.
</description>
</method>
<method name="is_cell_transposed" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="x" type="int">
</argument>
<argument index="1" name="y" type="int">
</argument>
<description>
Returns [code]true[/code] if the given cell is transposed, i.e. the X and Y axes are swapped.
</description>
</method>
<method name="is_cell_x_flipped" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="x" type="int">
</argument>
<argument index="1" name="y" type="int">
</argument>
<description>
Returns [code]true[/code] if the given cell is flipped in the X axis.
</description>
</method>
<method name="is_cell_y_flipped" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="x" type="int">
</argument>
<argument index="1" name="y" type="int">
</argument>
<description>
Returns [code]true[/code] if the given cell is flipped in the Y axis.
</description>
</method>
<method name="map_to_world" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="map_position" type="Vector2">
</argument>
<argument index="1" name="ignore_half_ofs" type="bool" default="false">
</argument>
<description>
Returns the local position corresponding to the given tilemap (grid-based) coordinates.
Optionally, the tilemap's half offset can be ignored.
</description>
</method>
<method name="set_cell">
<return type="void">
</return>
<argument index="0" name="x" type="int">
<argument index="0" name="coords" type="Vector2i">
</argument>
<argument index="1" name="y" type="int">
<argument index="1" name="source_id" type="int" default="-1">
</argument>
<argument index="2" name="tile" type="int">
<argument index="2" name="atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
<argument index="3" name="flip_x" type="bool" default="false">
</argument>
<argument index="4" name="flip_y" type="bool" default="false">
</argument>
<argument index="5" name="transpose" type="bool" default="false">
</argument>
<argument index="6" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )">
<argument index="3" name="alternative_tile" type="int" default="-1">
</argument>
<description>
Sets the tile index for the cell given by a Vector2.
An index of [code]-1[/code] clears the cell.
Optionally, the tile can also be flipped, transposed, or given autotile coordinates. The autotile coordinate refers to the column and row of the subtile.
[b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
If you need these to be immediately updated, you can call [method update_dirty_quadrants].
Overriding this method also overrides it internally, allowing custom logic to be implemented when tiles are placed/removed:
[codeblocks]
[gdscript]
func set_cell(x, y, tile, flip_x=false, flip_y=false, transpose=false, autotile_coord=Vector2()):
# Write your custom logic here.
# To call the default method:
.set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord)
[/gdscript]
[csharp]
public void SetCell(int x, int y, int tile, bool flipX = false, bool flipY = false, bool transpose = false, Vector2 autotileCoord = new Vector2())
{
// Write your custom logic here.
// To call the default method:
base.SetCell(x, y, tile, flipX, flipY, transpose, autotileCoord);
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="set_cellv">
<return type="void">
</return>
<argument index="0" name="position" type="Vector2">
</argument>
<argument index="1" name="tile" type="int">
</argument>
<argument index="2" name="flip_x" type="bool" default="false">
</argument>
<argument index="3" name="flip_y" type="bool" default="false">
</argument>
<argument index="4" name="transpose" type="bool" default="false">
</argument>
<description>
Sets the tile index for the given cell.
An index of [code]-1[/code] clears the cell.
Optionally, the tile can also be flipped or transposed.
[b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
If you need these to be immediately updated, you can call [method update_dirty_quadrants].
</description>
</method>
<method name="set_collision_layer_bit">
<return type="void">
</return>
<argument index="0" name="bit" type="int">
</argument>
<argument index="1" name="value" type="bool">
</argument>
<description>
Sets the given collision layer bit.
</description>
</method>
<method name="set_collision_mask_bit">
<return type="void">
</return>
<argument index="0" name="bit" type="int">
</argument>
<argument index="1" name="value" type="bool">
</argument>
<description>
Sets the given collision mask bit.
</description>
</method>
<method name="update_bitmask_area">
<return type="void">
</return>
<argument index="0" name="position" type="Vector2">
</argument>
<description>
Applies autotiling rules to the cell (and its adjacent cells) referenced by its grid-based X and Y coordinates.
</description>
</method>
<method name="update_bitmask_region">
<return type="void">
</return>
<argument index="0" name="start" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<argument index="1" name="end" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<description>
Applies autotiling rules to the cells in the given region (specified by grid-based X and Y coordinates).
Calling with invalid (or missing) parameters applies autotiling rules for the entire tilemap.
</description>
</method>
<method name="update_dirty_quadrants">
@ -262,7 +117,7 @@
</description>
</method>
<method name="world_to_map" qualifiers="const">
<return type="Vector2">
<return type="Vector2i">
</return>
<argument index="0" name="world_position" type="Vector2">
</argument>
@ -272,110 +127,19 @@
</method>
</methods>
<members>
<member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
If [code]true[/code], this TileMap bakes a navigation region.
</member>
<member name="cell_clip_uv" type="bool" setter="set_clip_uv" getter="get_clip_uv" default="false">
If [code]true[/code], the cell's UVs will be clipped.
</member>
<member name="cell_custom_transform" type="Transform2D" setter="set_custom_transform" getter="get_custom_transform" default="Transform2D( 64, 0, 0, 64, 0, 0 )">
The custom [Transform2D] to be applied to the TileMap's cells.
</member>
<member name="cell_half_offset" type="int" setter="set_half_offset" getter="get_half_offset" enum="TileMap.HalfOffset" default="2">
Amount to offset alternating tiles. See [enum HalfOffset] for possible values.
</member>
<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="cell_size" type="Vector2" setter="set_cell_size" getter="get_cell_size" default="Vector2( 64, 64 )">
The TileMap's cell size.
</member>
<member name="cell_tile_origin" type="int" setter="set_tile_origin" getter="get_tile_origin" enum="TileMap.TileOrigin" default="0">
Position for tile origin. See [enum TileOrigin] for possible values.
</member>
<member name="cell_y_sort" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
If [code]true[/code], the TileMap's direct children will be drawn in order of their Y coordinate.
</member>
<member name="centered_textures" type="bool" setter="set_centered_textures" getter="is_centered_textures_enabled" default="false">
If [code]true[/code], the textures will be centered in the middle of each tile. This is useful for certain isometric or top-down modes when textures are made larger or smaller than the tiles (e.g. to avoid flickering on tile edges). The offset is still applied, but from the center of the tile. If used, [member compatibility_mode] is ignored.
If [code]false[/code], the texture position start in the top-left corner unless [member compatibility_mode] is enabled.
</member>
<member name="collision_bounce" type="float" setter="set_collision_bounce" getter="get_collision_bounce" default="0.0">
Bounce value for static body collisions (see [code]collision_use_kinematic[/code]).
</member>
<member name="collision_friction" type="float" setter="set_collision_friction" getter="get_collision_friction" default="1.0">
Friction value for static body collisions (see [code]collision_use_kinematic[/code]).
</member>
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The collision layer(s) for all colliders in the TileMap. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
The collision mask(s) for all colliders in the TileMap. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_use_kinematic" type="bool" setter="set_collision_use_kinematic" getter="get_collision_use_kinematic" default="false">
If [code]true[/code], TileMap collisions will be handled as a kinematic body. If [code]false[/code], collisions will be handled as static body.
</member>
<member name="collision_use_parent" type="bool" setter="set_collision_use_parent" getter="get_collision_use_parent" default="false">
If [code]true[/code], this tilemap's collision shape will be added to the collision shape of the parent. The parent has to be a [CollisionObject2D].
</member>
<member name="compatibility_mode" type="bool" setter="set_compatibility_mode" getter="is_compatibility_mode_enabled" default="false">
If [code]true[/code], the compatibility with the tilemaps made in Godot 3.1 or earlier is maintained (textures move when the tile origin changes and rotate if the texture size is not homogeneous). This mode presents problems when doing [code]flip_h[/code], [code]flip_v[/code] and [code]transpose[/code] tile operations on non-homogeneous isometric tiles (e.g. 2:1), in which the texture could not coincide with the collision, thus it is not recommended for isometric or non-square tiles.
If [code]false[/code], the textures do not move when doing [code]flip_h[/code], [code]flip_v[/code] operations if no offset is used, nor when changing the tile origin.
The compatibility mode doesn't work with the [member centered_textures] option, because displacing textures with the [member cell_tile_origin] option or in irregular tiles is not relevant when centering those textures.
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="TileMap.Mode" default="0">
The TileMap orientation mode. See [enum Mode] for possible values.
</member>
<member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask" default="1">
The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s).
</member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet].
</member>
</members>
<signals>
<signal name="settings_changed">
<signal name="changed">
<description>
Emitted when a tilemap setting has changed.
</description>
</signal>
</signals>
<constants>
<constant name="INVALID_CELL" value="-1">
Returned when a cell doesn't exist.
</constant>
<constant name="MODE_SQUARE" value="0" enum="Mode">
Orthogonal orientation mode.
</constant>
<constant name="MODE_ISOMETRIC" value="1" enum="Mode">
Isometric orientation mode.
</constant>
<constant name="MODE_CUSTOM" value="2" enum="Mode">
Custom orientation mode.
</constant>
<constant name="HALF_OFFSET_X" value="0" enum="HalfOffset">
Half offset on the X coordinate.
</constant>
<constant name="HALF_OFFSET_Y" value="1" enum="HalfOffset">
Half offset on the Y coordinate.
</constant>
<constant name="HALF_OFFSET_DISABLED" value="2" enum="HalfOffset">
Half offset disabled.
</constant>
<constant name="HALF_OFFSET_NEGATIVE_X" value="3" enum="HalfOffset">
Half offset on the X coordinate (negative).
</constant>
<constant name="HALF_OFFSET_NEGATIVE_Y" value="4" enum="HalfOffset">
Half offset on the Y coordinate (negative).
</constant>
<constant name="TILE_ORIGIN_TOP_LEFT" value="0" enum="TileOrigin">
Tile origin at its top-left corner.
</constant>
<constant name="TILE_ORIGIN_CENTER" value="1" enum="TileOrigin">
Tile origin at its center.
</constant>
<constant name="TILE_ORIGIN_BOTTOM_LEFT" value="2" enum="TileOrigin">
Tile origin at its bottom-left corner.
</constant>
</constants>
</class>

View file

@ -17,752 +17,347 @@
<link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link>
</tutorials>
<methods>
<method name="_forward_atlas_subtile_selection" qualifiers="virtual">
<return type="Vector2">
<method name="add_source">
<return type="int">
</return>
<argument index="0" name="atlastile_id" type="int">
<argument index="0" name="atlas_source_id_override" type="TileSetAtlasSource">
</argument>
<argument index="1" name="tilemap" type="Object">
</argument>
<argument index="2" name="tile_location" type="Vector2">
<argument index="1" name="arg1" type="int" default="-1">
</argument>
<description>
</description>
</method>
<method name="_forward_subtile_selection" qualifiers="virtual">
<return type="Vector2">
<method name="get_navigation_layer_layers" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="autotile_id" type="int">
</argument>
<argument index="1" name="bitmask" type="int">
</argument>
<argument index="2" name="tilemap" type="Object">
</argument>
<argument index="3" name="tile_location" type="Vector2">
<argument index="0" name="layer_index" type="int">
</argument>
<description>
</description>
</method>
<method name="_is_tile_bound" qualifiers="virtual">
<method name="get_next_source_id" qualifiers="const">
<return type="int">
</return>
<description>
</description>
</method>
<method name="get_occlusion_layer_light_mask" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="arg0" type="int">
</argument>
<description>
</description>
</method>
<method name="get_occlusion_layer_sdf_collision" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="drawn_id" type="int">
</argument>
<argument index="1" name="neighbor_id" type="int">
<argument index="0" name="arg0" type="int">
</argument>
<description>
Determines when the auto-tiler should consider two different auto-tile IDs to be bound together.
[b]Note:[/b] [code]neighbor_id[/code] will be [code]-1[/code] ([constant TileMap.INVALID_CELL]) when checking a tile against an empty neighbor tile.
</description>
</method>
<method name="autotile_clear_bitmask_map">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Clears all bitmask information of the autotile.
</description>
</method>
<method name="autotile_get_bitmask">
<method name="get_physics_layer_collision_layer" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
<argument index="0" name="layer_index" type="int">
</argument>
<description>
Returns the bitmask of the subtile from an autotile given its coordinates.
The value is the sum of the values in [enum AutotileBindings] present in the subtile (e.g. a value of 5 means the bitmask has bindings in both the top left and top right).
</description>
</method>
<method name="autotile_get_bitmask_mode" qualifiers="const">
<return type="int" enum="TileSet.BitmaskMode">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the [enum BitmaskMode] of the autotile.
</description>
</method>
<method name="autotile_get_icon_coordinate" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the subtile that's being used as an icon in an atlas/autotile given its coordinates.
The subtile defined as the icon will be used as a fallback when the atlas/autotile's bitmask information is incomplete. It will also be used to represent it in the TileSet editor.
</description>
</method>
<method name="autotile_get_light_occluder" qualifiers="const">
<return type="OccluderPolygon2D">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
</argument>
<description>
Returns the light occluder of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="autotile_get_navigation_polygon" qualifiers="const">
<return type="NavigationPolygon">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
</argument>
<description>
Returns the navigation polygon of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="autotile_get_size" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the size of the subtiles in an atlas/autotile.
</description>
</method>
<method name="autotile_get_spacing" qualifiers="const">
<method name="get_physics_layer_collision_mask" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<description>
Returns the spacing between subtiles of the atlas/autotile.
</description>
</method>
<method name="autotile_get_subtile_priority">
<return type="int">
<method name="get_physics_layer_physics_material" qualifiers="const">
<return type="PhysicsMaterial">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
<argument index="0" name="layer_index" type="int">
</argument>
<description>
Returns the priority of the subtile from an autotile given its coordinates.
When more than one subtile has the same bitmask value, one of them will be picked randomly for drawing. Its priority will define how often it will be picked.
</description>
</method>
<method name="autotile_get_z_index">
<return type="int">
<method name="get_source" qualifiers="const">
<return type="TileSetSource">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
<argument index="0" name="index" type="int">
</argument>
<description>
Returns the drawing index of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="autotile_set_bitmask">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="bitmask" type="Vector2">
</argument>
<argument index="2" name="flag" type="int">
</argument>
<description>
Sets the bitmask of the subtile from an autotile given its coordinates.
The value is the sum of the values in [enum AutotileBindings] present in the subtile (e.g. a value of 5 means the bitmask has bindings in both the top left and top right).
</description>
</method>
<method name="autotile_set_bitmask_mode">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="mode" type="int" enum="TileSet.BitmaskMode">
</argument>
<description>
Sets the [enum BitmaskMode] of the autotile.
</description>
</method>
<method name="autotile_set_icon_coordinate">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
</argument>
<description>
Sets the subtile that will be used as an icon in an atlas/autotile given its coordinates.
The subtile defined as the icon will be used as a fallback when the atlas/autotile's bitmask information is incomplete. It will also be used to represent it in the TileSet editor.
</description>
</method>
<method name="autotile_set_light_occluder">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="light_occluder" type="OccluderPolygon2D">
</argument>
<argument index="2" name="coord" type="Vector2">
</argument>
<description>
Sets the light occluder of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="autotile_set_navigation_polygon">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="navigation_polygon" type="NavigationPolygon">
</argument>
<argument index="2" name="coord" type="Vector2">
</argument>
<description>
Sets the navigation polygon of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="autotile_set_size">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="size" type="Vector2">
</argument>
<description>
Sets the size of the subtiles in an atlas/autotile.
</description>
</method>
<method name="autotile_set_spacing">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="spacing" type="int">
</argument>
<description>
Sets the spacing between subtiles of the atlas/autotile.
</description>
</method>
<method name="autotile_set_subtile_priority">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
</argument>
<argument index="2" name="priority" type="int">
</argument>
<description>
Sets the priority of the subtile from an autotile given its coordinates.
When more than one subtile has the same bitmask value, one of them will be picked randomly for drawing. Its priority will define how often it will be picked.
</description>
</method>
<method name="autotile_set_z_index">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="coord" type="Vector2">
</argument>
<argument index="2" name="z_index" type="int">
</argument>
<description>
Sets the drawing index of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
<method name="clear">
<return type="void">
</return>
<description>
Clears all tiles.
</description>
</method>
<method name="create_tile">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Creates a new tile with the given ID.
</description>
</method>
<method name="find_tile_by_name" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="name" type="String">
</argument>
<description>
Returns the first tile matching the given name.
</description>
</method>
<method name="get_last_unused_tile_id" qualifiers="const">
<method name="get_source_count" qualifiers="const">
<return type="int">
</return>
<description>
Returns the ID following the last currently used ID, useful when creating a new tile.
</description>
</method>
<method name="get_tiles_ids" qualifiers="const">
<return type="Array">
<method name="get_source_id" qualifiers="const">
<return type="int">
</return>
<description>
Returns an array of all currently used tile IDs.
</description>
</method>
<method name="remove_tile">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="index" type="int">
</argument>
<description>
Removes the given tile ID.
</description>
</method>
<method name="tile_add_shape">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape" type="Shape2D">
</argument>
<argument index="2" name="shape_transform" type="Transform2D">
</argument>
<argument index="3" name="one_way" type="bool" default="false">
</argument>
<argument index="4" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )">
</argument>
<description>
Adds a shape to the tile.
</description>
</method>
<method name="tile_get_light_occluder" qualifiers="const">
<return type="OccluderPolygon2D">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile's light occluder.
</description>
</method>
<method name="tile_get_material" qualifiers="const">
<return type="ShaderMaterial">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile's material.
</description>
</method>
<method name="tile_get_modulate" qualifiers="const">
<method name="get_terrain_color" qualifiers="const">
<return type="Color">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="terrain_index" type="int">
</argument>
<description>
Returns the tile's modulation color.
</description>
</method>
<method name="tile_get_name" qualifiers="const">
<method name="get_terrain_name" qualifiers="const">
<return type="String">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="terrain_index" type="int">
</argument>
<description>
Returns the tile's name.
</description>
</method>
<method name="tile_get_navigation_polygon" qualifiers="const">
<return type="NavigationPolygon">
<method name="get_terrain_set_mode" qualifiers="const">
<return type="int" enum="TileSet.TerrainMode">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="terrain_set" type="int">
</argument>
<description>
Returns the navigation polygon of the tile.
</description>
</method>
<method name="tile_get_navigation_polygon_offset" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the offset of the tile's navigation polygon.
</description>
</method>
<method name="tile_get_occluder_offset" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the offset of the tile's light occluder.
</description>
</method>
<method name="tile_get_region" qualifiers="const">
<return type="Rect2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile sub-region in the texture.
</description>
</method>
<method name="tile_get_shape" qualifiers="const">
<return type="Shape2D">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<description>
Returns a tile's given shape.
</description>
</method>
<method name="tile_get_shape_count" qualifiers="const">
<method name="get_terrains_count" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="terrain_set" type="int">
</argument>
<description>
Returns the number of shapes assigned to a tile.
</description>
</method>
<method name="tile_get_shape_offset" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<description>
Returns the offset of a tile's shape.
</description>
</method>
<method name="tile_get_shape_one_way" qualifiers="const">
<method name="has_source" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<description>
Returns the one-way collision value of a tile's shape.
</description>
</method>
<method name="tile_get_shape_one_way_margin" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
<argument index="0" name="index" type="int">
</argument>
<description>
</description>
</method>
<method name="tile_get_shape_transform" qualifiers="const">
<return type="Transform2D">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<description>
Returns the [Transform2D] of a tile's shape.
</description>
</method>
<method name="tile_get_shapes" qualifiers="const">
<return type="Array">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns an array of dictionaries describing the tile's shapes.
[b]Dictionary structure in the array returned by this method:[/b]
[codeblock]
{
"autotile_coord": Vector2,
"one_way": bool,
"one_way_margin": int,
"shape": CollisionShape2D,
"shape_transform": Transform2D,
}
[/codeblock]
</description>
</method>
<method name="tile_get_texture" qualifiers="const">
<return type="Texture2D">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile's texture.
</description>
</method>
<method name="tile_get_texture_offset" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the texture offset of the tile.
</description>
</method>
<method name="tile_get_tile_mode" qualifiers="const">
<return type="int" enum="TileSet.TileMode">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile's [enum TileMode].
</description>
</method>
<method name="tile_get_z_index" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="id" type="int">
</argument>
<description>
Returns the tile's Z index (drawing layer).
</description>
</method>
<method name="tile_set_light_occluder">
<method name="remove_source">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="light_occluder" type="OccluderPolygon2D">
</argument>
<description>
Sets a light occluder for the tile.
</description>
</method>
<method name="tile_set_material">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="material" type="ShaderMaterial">
</argument>
<description>
Sets the tile's material.
</description>
</method>
<method name="tile_set_modulate">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="color" type="Color">
</argument>
<description>
Sets the tile's modulation color.
</description>
</method>
<method name="tile_set_name">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="name" type="String">
</argument>
<description>
Sets the tile's name.
</description>
</method>
<method name="tile_set_navigation_polygon">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="navigation_polygon" type="NavigationPolygon">
</argument>
<description>
Sets the tile's navigation polygon.
</description>
</method>
<method name="tile_set_navigation_polygon_offset">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="navigation_polygon_offset" type="Vector2">
</argument>
<description>
Sets an offset for the tile's navigation polygon.
</description>
</method>
<method name="tile_set_occluder_offset">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="occluder_offset" type="Vector2">
</argument>
<description>
Sets an offset for the tile's light occluder.
</description>
</method>
<method name="tile_set_region">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="region" type="Rect2">
</argument>
<description>
Sets the tile's sub-region in the texture. This is common in texture atlases.
</description>
</method>
<method name="tile_set_shape">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<argument index="2" name="shape" type="Shape2D">
</argument>
<description>
Sets a shape for the tile, enabling collision.
</description>
</method>
<method name="tile_set_shape_offset">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<argument index="2" name="shape_offset" type="Vector2">
</argument>
<description>
Sets the offset of a tile's shape.
</description>
</method>
<method name="tile_set_shape_one_way">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<argument index="2" name="one_way" type="bool">
</argument>
<description>
Enables one-way collision on a tile's shape.
</description>
</method>
<method name="tile_set_shape_one_way_margin">
<return type="void">
</return>
<argument index="0" name="id" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<argument index="2" name="one_way" type="float">
<argument index="0" name="source_id" type="int">
</argument>
<description>
</description>
</method>
<method name="tile_set_shape_transform">
<method name="set_navigation_layer_layers">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="shape_id" type="int">
</argument>
<argument index="2" name="shape_transform" type="Transform2D">
<argument index="1" name="layers" type="int">
</argument>
<description>
Sets a [Transform2D] on a tile's shape.
</description>
</method>
<method name="tile_set_shapes">
<method name="set_occlusion_layer_light_mask">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="shapes" type="Array">
<argument index="1" name="light_mask" type="int">
</argument>
<description>
Sets an array of shapes for the tile, enabling collision.
</description>
</method>
<method name="tile_set_texture">
<method name="set_occlusion_layer_sdf_collision">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="texture" type="Texture2D">
<argument index="1" name="sdf_collision" type="int">
</argument>
<description>
Sets the tile's texture.
</description>
</method>
<method name="tile_set_texture_offset">
<method name="set_physics_layer_collision_layer">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="texture_offset" type="Vector2">
<argument index="1" name="layer" type="int">
</argument>
<description>
Sets the tile's texture offset.
</description>
</method>
<method name="tile_set_tile_mode">
<method name="set_physics_layer_collision_mask">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="tilemode" type="int" enum="TileSet.TileMode">
<argument index="1" name="mask" type="int">
</argument>
<description>
Sets the tile's [enum TileMode].
</description>
</method>
<method name="tile_set_z_index">
<method name="set_physics_layer_physics_material">
<return type="void">
</return>
<argument index="0" name="id" type="int">
<argument index="0" name="layer_index" type="int">
</argument>
<argument index="1" name="z_index" type="int">
<argument index="1" name="physics_material" type="PhysicsMaterial">
</argument>
<description>
</description>
</method>
<method name="set_source_id">
<return type="void">
</return>
<argument index="0" name="source_id" type="int">
</argument>
<argument index="1" name="arg1" type="int">
</argument>
<description>
</description>
</method>
<method name="set_terrain_color">
<return type="void">
</return>
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="terrain_index" type="int">
</argument>
<argument index="2" name="color" type="Color">
</argument>
<description>
</description>
</method>
<method name="set_terrain_name">
<return type="void">
</return>
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="terrain_index" type="int">
</argument>
<argument index="2" name="name" type="String">
</argument>
<description>
</description>
</method>
<method name="set_terrain_set_mode">
<return type="void">
</return>
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="mode" type="int" enum="TileSet.TerrainMode">
</argument>
<description>
</description>
</method>
<method name="set_terrains_count">
<return type="void">
</return>
<argument index="0" name="terrain_set" type="int">
</argument>
<argument index="1" name="terrains_count" type="int">
</argument>
<description>
Sets the tile's drawing index.
</description>
</method>
</methods>
<members>
<member name="custom_data_layers_count" type="int" setter="set_custom_data_layers_count" getter="get_custom_data_layers_count" default="0">
</member>
<member name="navigation_layers_count" type="int" setter="set_navigation_layers_count" getter="get_navigation_layers_count" default="0">
</member>
<member name="occlusion_layers_count" type="int" setter="set_occlusion_layers_count" getter="get_occlusion_layers_count" default="0">
</member>
<member name="physics_layers_count" type="int" setter="set_physics_layers_count" getter="get_physics_layers_count" default="0">
</member>
<member name="terrains_sets_count" type="int" setter="set_terrain_sets_count" getter="get_terrain_sets_count" default="0">
</member>
<member name="tile_layout" type="int" setter="set_tile_layout" getter="get_tile_layout" enum="TileSet.TileLayout" default="0">
</member>
<member name="tile_offset_axis" type="int" setter="set_tile_offset_axis" getter="get_tile_offset_axis" enum="TileSet.TileOffsetAxis" default="0">
</member>
<member name="tile_shape" type="int" setter="set_tile_shape" getter="get_tile_shape" enum="TileSet.TileShape" default="0">
</member>
<member name="tile_size" type="Vector2i" setter="set_tile_size" getter="get_tile_size" default="Vector2i( 16, 16 )">
</member>
<member name="tile_skew" type="Vector2" setter="set_tile_skew" getter="get_tile_skew" default="Vector2( 0, 0 )">
</member>
<member name="uv_clipping" type="bool" setter="set_uv_clipping" getter="is_uv_clipping" default="false">
</member>
<member name="y_sorting" type="bool" setter="set_y_sorting" getter="is_y_sorting" default="false">
</member>
</members>
<constants>
<constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
<constant name="TILE_SHAPE_SQUARE" value="0" enum="TileShape">
</constant>
<constant name="BITMASK_3X3_MINIMAL" value="1" enum="BitmaskMode">
<constant name="TILE_SHAPE_ISOMETRIC" value="1" enum="TileShape">
</constant>
<constant name="BITMASK_3X3" value="2" enum="BitmaskMode">
<constant name="TILE_SHAPE_HALF_OFFSET_SQUARE" value="2" enum="TileShape">
</constant>
<constant name="BIND_TOPLEFT" value="1" enum="AutotileBindings">
<constant name="TILE_SHAPE_HEXAGON" value="3" enum="TileShape">
</constant>
<constant name="BIND_TOP" value="2" enum="AutotileBindings">
<constant name="TILE_LAYOUT_STACKED" value="0" enum="TileLayout">
</constant>
<constant name="BIND_TOPRIGHT" value="4" enum="AutotileBindings">
<constant name="TILE_LAYOUT_STACKED_OFFSET" value="1" enum="TileLayout">
</constant>
<constant name="BIND_LEFT" value="8" enum="AutotileBindings">
<constant name="TILE_LAYOUT_STAIRS_RIGHT" value="2" enum="TileLayout">
</constant>
<constant name="BIND_CENTER" value="16" enum="AutotileBindings">
<constant name="TILE_LAYOUT_STAIRS_DOWN" value="3" enum="TileLayout">
</constant>
<constant name="BIND_RIGHT" value="32" enum="AutotileBindings">
<constant name="TILE_LAYOUT_DIAMOND_RIGHT" value="4" enum="TileLayout">
</constant>
<constant name="BIND_BOTTOMLEFT" value="64" enum="AutotileBindings">
<constant name="TILE_LAYOUT_DIAMOND_DOWN" value="5" enum="TileLayout">
</constant>
<constant name="BIND_BOTTOM" value="128" enum="AutotileBindings">
<constant name="TILE_OFFSET_AXIS_HORIZONTAL" value="0" enum="TileOffsetAxis">
</constant>
<constant name="BIND_BOTTOMRIGHT" value="256" enum="AutotileBindings">
<constant name="TILE_OFFSET_AXIS_VERTICAL" value="1" enum="TileOffsetAxis">
</constant>
<constant name="SINGLE_TILE" value="0" enum="TileMode">
<constant name="TileSet::CELL_NEIGHBOR_RIGHT_SIDE" value="0" enum="CellNeighbor">
</constant>
<constant name="AUTO_TILE" value="1" enum="TileMode">
<constant name="TileSet::CELL_NEIGHBOR_RIGHT_CORNER" value="1" enum="CellNeighbor">
</constant>
<constant name="ATLAS_TILE" value="2" enum="TileMode">
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE" value="2" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER" value="3" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_SIDE" value="4" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_CORNER" value="5" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE" value="6" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER" value="7" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_LEFT_SIDE" value="8" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_LEFT_CORNER" value="9" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE" value="10" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER" value="11" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_SIDE" value="12" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_CORNER" value="13" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE" value="14" enum="CellNeighbor">
</constant>
<constant name="TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER" value="15" enum="CellNeighbor">
</constant>
<constant name="TERRAIN_MODE_MATCH_CORNERS_AND_SIDES" value="0" enum="TerrainMode">
</constant>
<constant name="TERRAIN_MODE_MATCH_CORNERS" value="1" enum="TerrainMode">
</constant>
<constant name="TERRAIN_MODE_MATCH_SIDES" value="2" enum="TerrainMode">
</constant>
</constants>
</class>

View file

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TileSetAtlasSource" inherits="TileSetSource" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="can_move_tile_in_atlas" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
<argument index="2" name="new_size" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
<description>
</description>
</method>
<method name="clear_tiles_outside_texture">
<return type="void">
</return>
<description>
</description>
</method>
<method name="create_alternative_tile">
<return type="int">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="alternative_id_override" type="int" default="-1">
</argument>
<description>
</description>
</method>
<method name="create_tile">
<return type="void">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="size" type="Vector2i" default="Vector2i( 1, 1 )">
</argument>
<description>
</description>
</method>
<method name="get_alternative_tile_id" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="index" type="int">
</argument>
<description>
</description>
</method>
<method name="get_alternative_tiles_count" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="get_atlas_grid_size" qualifiers="const">
<return type="Vector2i">
</return>
<description>
</description>
</method>
<method name="get_next_alternative_tile_id" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="get_tile_at_coords" qualifiers="const">
<return type="Vector2i">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="get_tile_data" qualifiers="const">
<return type="Object">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="index" type="int">
</argument>
<description>
</description>
</method>
<method name="get_tile_id" qualifiers="const">
<return type="Vector2i">
</return>
<argument index="0" name="index" type="int">
</argument>
<description>
</description>
</method>
<method name="get_tile_size_in_atlas" qualifiers="const">
<return type="Vector2i">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="get_tile_texture_region" qualifiers="const">
<return type="Rect2i">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="get_tiles_count" qualifiers="const">
<return type="int">
</return>
<description>
</description>
</method>
<method name="has_alternative_tile" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="alternative_tile" type="int">
</argument>
<description>
</description>
</method>
<method name="has_tile" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="has_tiles_outside_texture">
<return type="bool">
</return>
<description>
</description>
</method>
<method name="move_tile_in_atlas">
<return type="void">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
<argument index="2" name="new_size" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
<description>
</description>
</method>
<method name="remove_alternative_tile">
<return type="void">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="alternative_tile" type="int">
</argument>
<description>
</description>
</method>
<method name="remove_tile">
<return type="void">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<description>
</description>
</method>
<method name="set_alternative_tile_id">
<return type="void">
</return>
<argument index="0" name="atlas_coords" type="Vector2i">
</argument>
<argument index="1" name="alternative_tile" type="int">
</argument>
<argument index="2" name="new_id" type="int">
</argument>
<description>
</description>
</method>
</methods>
<members>
<member name="margins" type="Vector2i" setter="set_margins" getter="get_margins" default="Vector2i( 0, 0 )">
</member>
<member name="separation" type="Vector2i" setter="set_separation" getter="get_separation" default="Vector2i( 0, 0 )">
</member>
<member name="texture" type="Texture2D" setter="set_texture" getter="get_texture">
</member>
<member name="tile_size" type="Vector2i" setter="set_texture_region_size" getter="get_texture_region_size" default="Vector2i( 16, 16 )">
</member>
</members>
<constants>
</constants>
</class>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="TileSetSource" inherits="Resource" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
</methods>
<constants>
</constants>
</class>

View file

@ -170,8 +170,7 @@
#include "editor/plugins/texture_layered_editor_plugin.h"
#include "editor/plugins/texture_region_editor_plugin.h"
#include "editor/plugins/theme_editor_plugin.h"
#include "editor/plugins/tile_map_editor_plugin.h"
#include "editor/plugins/tile_set_editor_plugin.h"
#include "editor/plugins/tiles/tiles_editor_plugin.h"
#include "editor/plugins/version_control_editor_plugin.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/progress_dialog.h"
@ -1804,28 +1803,6 @@ void EditorNode::_dialog_action(String p_file) {
}
} break;
case FILE_EXPORT_TILESET: {
Ref<TileSet> tileset;
if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) {
tileset = ResourceLoader::load(p_file, "TileSet");
if (tileset.is_null()) {
show_accept(TTR("Can't load TileSet for merging!"), TTR("OK"));
return;
}
} else {
tileset = Ref<TileSet>(memnew(TileSet));
}
TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true);
Error err = ResourceSaver::save(p_file, tileset);
if (err) {
show_accept(TTR("Error saving TileSet!"), TTR("OK"));
return;
}
} break;
case RESOURCE_SAVE:
case RESOURCE_SAVE_AS: {
@ -2539,25 +2516,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
file_export_lib->popup_file_dialog();
file_export_lib->set_title(TTR("Export Mesh Library"));
} break;
case FILE_EXPORT_TILESET: {
//Make sure that the scene has a root before trying to convert to tileset
if (!editor_data.get_edited_scene_root()) {
show_accept(TTR("This operation can't be done without a root node."), TTR("OK"));
break;
}
List<String> extensions;
Ref<TileSet> ml(memnew(TileSet));
ResourceSaver::get_recognized_extensions(ml, &extensions);
file_export_lib->clear_filters();
for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
file_export_lib->add_filter("*." + E->get());
}
file_export_lib->popup_file_dialog();
file_export_lib->set_title(TTR("Export Tile Set"));
} break;
case FILE_EXTERNAL_OPEN_SCENE: {
@ -5902,7 +5860,7 @@ EditorNode::EditorNode() {
EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true);
EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "Script,MeshLibrary,TileSet");
EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "Script,MeshLibrary");
EDITOR_DEF("interface/inspector/default_color_picker_mode", 0);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW", PROPERTY_USAGE_DEFAULT));
EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_VHS_CIRCLE);
@ -6254,7 +6212,6 @@ EditorNode::EditorNode() {
p->add_child(pm_export);
p->add_submenu_item(TTR("Convert To..."), "Export");
pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY);
pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet...")), FILE_EXPORT_TILESET);
pm_export->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
p->add_separator();
@ -6827,8 +6784,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this)));
add_editor_plugin(memnew(TileSetEditorPlugin(this)));
add_editor_plugin(memnew(TileMapEditorPlugin(this)));
add_editor_plugin(memnew(TilesEditorPlugin(this)));
add_editor_plugin(memnew(SpriteFramesEditorPlugin(this)));
add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
add_editor_plugin(memnew(GIProbeEditorPlugin(this)));

View file

@ -135,7 +135,6 @@ private:
FILE_EXPORT_MESH_LIBRARY,
FILE_INSTALL_ANDROID_SOURCE,
FILE_EXPLORE_ANDROID_BUILD_TEMPLATES,
FILE_EXPORT_TILESET,
FILE_SAVE_OPTIMIZED,
FILE_OPEN_RECENT,
FILE_OPEN_OLD_SCENE,

View file

@ -0,0 +1,163 @@
/*************************************************************************/
/* editor_zoom_widget.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_zoom_widget.h"
#include "core/os/keyboard.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
void EditorZoomWidget::_update_zoom_label() {
String zoom_text;
// The zoom level displayed is relative to the editor scale
// (like in most image editors). Its lower bound is clamped to 1 as some people
// lower the editor scale to increase the available real estate,
// even if their display doesn't have a particularly low DPI.
if (zoom >= 10) {
// Don't show a decimal when the zoom level is higher than 1000 %.
zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign();
} else {
zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign();
}
zoom_reset->set_text(zoom_text);
}
void EditorZoomWidget::_button_zoom_minus() {
set_zoom_by_increments(-6);
emit_signal("zoom_changed", zoom);
}
void EditorZoomWidget::_button_zoom_reset() {
set_zoom(1.0);
emit_signal("zoom_changed", zoom);
}
void EditorZoomWidget::_button_zoom_plus() {
set_zoom_by_increments(6);
emit_signal("zoom_changed", zoom);
}
float EditorZoomWidget::get_zoom() {
return zoom;
}
void EditorZoomWidget::set_zoom(float p_zoom) {
if (p_zoom > 0 && p_zoom != zoom) {
zoom = p_zoom;
_update_zoom_label();
}
}
void EditorZoomWidget::set_zoom_by_increments(int p_increment_count) {
// Base increment factor defined as the twelveth root of two.
// This allow a smooth geometric evolution of the zoom, with the advantage of
// visiting all integer power of two scale factors.
// note: this is analogous to the 'semitones' interval in the music world
// In order to avoid numerical imprecisions, we compute and edit a zoom index
// with the following relation: zoom = 2 ^ (index / 12)
if (zoom < CMP_EPSILON || p_increment_count == 0) {
return;
}
// Remove Editor scale from the index computation
float zoom_noscale = zoom / MAX(1, EDSCALE);
// zoom = 2**(index/12) => log2(zoom) = index/12
float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
float new_zoom_index = closest_zoom_index + p_increment_count;
float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
// Restore Editor scale transformation
new_zoom *= MAX(1, EDSCALE);
set_zoom(new_zoom);
}
void EditorZoomWidget::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED:
zoom_minus->set_icon(get_theme_icon("ZoomLess", "EditorIcons"));
zoom_plus->set_icon(get_theme_icon("ZoomMore", "EditorIcons"));
break;
default:
break;
}
}
void EditorZoomWidget::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &EditorZoomWidget::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &EditorZoomWidget::get_zoom);
ClassDB::bind_method(D_METHOD("set_zoom_by_increments", "increment"), &EditorZoomWidget::set_zoom_by_increments);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
ADD_SIGNAL(MethodInfo("zoom_changed", PropertyInfo(Variant::FLOAT, "zoom")));
}
EditorZoomWidget::EditorZoomWidget() {
// Zoom buttons
zoom_minus = memnew(Button);
zoom_minus->set_flat(true);
add_child(zoom_minus);
zoom_minus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_minus));
zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom Out"), KEY_MASK_CMD | KEY_MINUS));
zoom_minus->set_shortcut_context(this);
zoom_minus->set_focus_mode(FOCUS_NONE);
zoom_reset = memnew(Button);
zoom_reset->set_flat(true);
add_child(zoom_reset);
zoom_reset->add_theme_constant_override("outline_size", 1);
zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0));
zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1));
zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset));
zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset"), KEY_MASK_CMD | KEY_0));
zoom_reset->set_shortcut_context(this);
zoom_reset->set_focus_mode(FOCUS_NONE);
zoom_reset->set_text_align(Button::TextAlign::ALIGN_CENTER);
// Prevent the button's size from changing when the text size changes
zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
zoom_plus = memnew(Button);
zoom_plus->set_flat(true);
add_child(zoom_plus);
zoom_plus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_plus));
zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom In"), KEY_MASK_CMD | KEY_EQUAL)); // Usually direct access key for PLUS
zoom_plus->set_shortcut_context(this);
zoom_plus->set_focus_mode(FOCUS_NONE);
_update_zoom_label();
add_theme_constant_override("separation", Math::round(-8 * EDSCALE));
}

View file

@ -0,0 +1,62 @@
/*************************************************************************/
/* editor_zoom_widget.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_ZOOM_WIDGET_H
#define EDITOR_ZOOM_WIDGET_H
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
class EditorZoomWidget : public HBoxContainer {
GDCLASS(EditorZoomWidget, HBoxContainer);
Button *zoom_minus;
Button *zoom_reset;
Button *zoom_plus;
float zoom = 1.0;
void _update_zoom_label();
void _button_zoom_minus();
void _button_zoom_reset();
void _button_zoom_plus();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
EditorZoomWidget();
float get_zoom();
void set_zoom(float p_zoom);
void set_zoom_by_increments(int p_increment_count);
};
#endif // EDITOR_ZOOM_WIDGET_H

View file

@ -1 +0,0 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#c9cfd4"/></svg>

Before

Width:  |  Height:  |  Size: 149 B

View file

@ -1 +0,0 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#4490fc"/></svg>

Before

Width:  |  Height:  |  Size: 149 B

View file

@ -1 +0,0 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#fce844"/></svg>

Before

Width:  |  Height:  |  Size: 149 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.5105509 1c-.554 0-1 .446-1 1v2h4v-2c0-.554-.446-1-1-1zm5.4894491 12.5v1.5h6v-1.5zm-6.4894491-8.5v7l2 3 2-3v-7zm1 1h1v5h-1zm7.7394491 0v2.25h-2.25v1.5h2.25v2.25h1.5v-2.25h2.25v-1.5h-2.25v-2.25z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 312 B

View file

@ -0,0 +1 @@
<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><path d="m5 0c-2.7614237 0-5 2.2385763-5 5s2.2385763 5 5 5 5-2.2385763 5-5-2.2385763-5-5-5zm-.0421327 2.2165215c.014044-.0001063.0280887-.0001063.0421327 0 1.5372727 0 2.7834785 1.2462058 2.7834785 2.7834785s-1.2462058 2.7834785-2.7834785 2.7834785-2.7834785-1.2462058-2.7834785-2.7834785c-.0001743-1.5209681 1.2205519-2.760456 2.7413458-2.7834785z" fill="#fff" fill-opacity=".294118"/></svg>

After

Width:  |  Height:  |  Size: 476 B

1
editor/icons/Eraser.svg Normal file
View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m10.228155 1.5447161-9.60250173 9.6107429 1.41421353 1.414213 1.6134635 1.885612.00693-.0069 4.2288056.000024 7.4852811-7.4852817zm-4.4456043 7.2823178 2.3136653 2.5858141-1.0357479 1.035746h-2.5853592l-1.0209853-1.293133z" fill="#e0e0e0" fill-opacity=".996078" stroke-width="1.02405"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2c-.5522619.0000552-.9999448.4477381-1 1v10c.0000552.552262.4477381.999945 1 1h6v-2h-5v-8h10v1h2v-2c-.000055-.5522619-.447738-.9999448-1-1zm9.25 4v2.25h-2.25v1.5h2.25v2.25h1.5v-2.25h2.25v-1.5h-2.25v-2.25zm-2.25 7.5v1.5h6v-1.5z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 346 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m15 1h-6v3h3v3h3z" fill="#ffa62a"/><path d="m1 1h6v3h-2.9999996l-.0000004 3h-3.0000004z" fill="#1aab1a"/><path d="m1 15h5.9999999v-3h-3v-3h-2.9999999z" fill="#ffa62a"/><path d="m15.000001 15h-6v-3h2.999999l.000001-2.9999997h3z" fill="#1aab1a"/></g></svg>

After

Width:  |  Height:  |  Size: 373 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1h4v3h-1l-1 1-1-1h-1z" fill="#ffa62a" stroke-width="70.7093"/><path d="m1 15h4v-3h-1v-1h-3z" fill="#1aab1a" stroke-width="70.7093"/><path d="m6 15h4v-3h-1l-1-1-1 1h-1z" fill="#ffa62a" stroke-width="99.998"/><g stroke-width="70.7093"><path d="m1 10v-4h3v1l1 1-1 1v1z" fill="#ffa62a"/><path d="m15 10v-4h-3v1l-1 1 1 1v1z" fill="#ffa62a"/><g fill="#1aab1a"><path d="m15 15h-4v-3h1v-1h3z"/><path d="m15 1h-4v3h1v1h3z"/><path d="m1 1h4.0000004v3h-1v1h-3.0000004z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 570 B

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m1 14v-12l3 3v2l1 1-1 1v2z" fill="#1aab1a"/><path d="m15 14v-12l-3 2.7057075v2l-1 1.0000001 1 1v1.9999994z" fill="#1aab1a"/><g fill="#ffa62a"><path d="m2 15h12l-3-3h-2l-1-1-1 1h-2z"/><path d="m14 1h-12l2.9999992 3h1.9999998l1.000001.9999999 1-.9999999h1.999999z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 397 B

View file

@ -0,0 +1 @@
<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v8l8-8zm16 0-16 16v8l24-24zm16 0-32 32v8l40-40zm16 0-48 48v8l56-56zm16 0-64 64h8l56-56zm0 16-48 48h8l40-40zm0 16-32 32h8s24-23 24-24zm0 16-16 16h8l8-8z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 269 B

View file

@ -3,3 +3,5 @@
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")
SConscript("tiles/SCsub")

View file

@ -1296,11 +1296,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
float new_zoom = _get_next_zoom_value(-1);
zoom_widget->set_zoom_by_increments(-1);
if (b->get_factor() != 1.f) {
new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f);
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
_zoom_on_position(new_zoom, b->get_position());
_zoom_on_position(zoom_widget->get_zoom(), b->get_position());
}
return true;
}
@ -1311,11 +1311,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
float new_zoom = _get_next_zoom_value(1);
zoom_widget->set_zoom_by_increments(1);
if (b->get_factor() != 1.f) {
new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f);
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
_zoom_on_position(new_zoom, b->get_position());
_zoom_on_position(zoom_widget->get_zoom(), b->get_position());
}
return true;
}
@ -1391,12 +1391,13 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
// If control key pressed, then zoom instead of pan
if (pan_gesture->get_control()) {
const float factor = pan_gesture->get_delta().y;
float new_zoom = _get_next_zoom_value(-1);
zoom_widget->set_zoom_by_increments(1);
if (factor != 1.f) {
new_zoom = zoom * ((new_zoom / zoom - 1.f) * factor + 1.f);
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * factor + 1.f));
}
_zoom_on_position(new_zoom, pan_gesture->get_position());
_zoom_on_position(zoom_widget->get_zoom(), pan_gesture->get_position());
return true;
}
@ -4219,9 +4220,6 @@ void CanvasItemEditor::_notification(int p_what) {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_icon(get_theme_icon("GuiTabMenuHl", "EditorIcons"));
zoom_minus->set_icon(get_theme_icon("ZoomLess", "EditorIcons"));
zoom_plus->set_icon(get_theme_icon("ZoomMore", "EditorIcons"));
presets_menu->set_icon(get_theme_icon("ControlLayout", "EditorIcons"));
PopupMenu *p = presets_menu->get_popup();
@ -4579,33 +4577,6 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) {
undo_redo->commit_action();
}
float CanvasItemEditor::_get_next_zoom_value(int p_increment_count) const {
// Base increment factor defined as the twelveth root of two.
// This allow a smooth geometric evolution of the zoom, with the advantage of
// visiting all integer power of two scale factors.
// note: this is analogous to the 'semitones' interval in the music world
// In order to avoid numerical imprecisions, we compute and edit a zoom index
// with the following relation: zoom = 2 ^ (index / 12)
if (zoom < CMP_EPSILON || p_increment_count == 0) {
return 1.f;
}
// Remove Editor scale from the index computation
float zoom_noscale = zoom / MAX(1, EDSCALE);
// zoom = 2**(index/12) => log2(zoom) = index/12
float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
float new_zoom_index = closest_zoom_index + p_increment_count;
float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
// Restore Editor scale transformation
new_zoom *= MAX(1, EDSCALE);
return new_zoom;
}
void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM);
@ -4630,36 +4601,12 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
view_offset = view_offset_int + (view_offset_frac * closest_zoom_factor).round() / closest_zoom_factor;
}
_update_zoom_label();
zoom_widget->set_zoom(zoom);
update_viewport();
}
void CanvasItemEditor::_update_zoom_label() {
String zoom_text;
// The zoom level displayed is relative to the editor scale
// (like in most image editors). Its lower bound is clamped to 1 as some people
// lower the editor scale to increase the available real estate,
// even if their display doesn't have a particularly low DPI.
if (zoom >= 10) {
// Don't show a decimal when the zoom level is higher than 1000 %.
zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign();
} else {
zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign();
}
zoom_reset->set_text(zoom_text);
}
void CanvasItemEditor::_button_zoom_minus() {
_zoom_on_position(_get_next_zoom_value(-6), viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_zoom_reset() {
_zoom_on_position(1.0 * MAX(1, EDSCALE), viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_zoom_plus() {
_zoom_on_position(_get_next_zoom_value(6), viewport_scrollable->get_size() / 2.0);
void CanvasItemEditor::_update_zoom(float p_zoom) {
_zoom_on_position(p_zoom, viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_toggle_smart_snap(bool p_status) {
@ -5401,7 +5348,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
zoom = scale_x < scale_y ? scale_x : scale_y;
zoom *= 0.90;
viewport->update();
_update_zoom_label();
zoom_widget->set_zoom(zoom);
call_deferred("_popup_callback", VIEW_CENTER_TO_SELECTION);
}
}
@ -5446,7 +5393,7 @@ Dictionary CanvasItemEditor::get_state() const {
state["show_rulers"] = show_rulers;
state["show_guides"] = show_guides;
state["show_helpers"] = show_helpers;
state["show_zoom_control"] = zoom_hb->is_visible();
state["show_zoom_control"] = zoom_widget->is_visible();
state["show_edit_locks"] = show_edit_locks;
state["show_transformation_gizmos"] = show_transformation_gizmos;
state["snap_rotation"] = snap_rotation;
@ -5464,7 +5411,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
// Compensate the editor scale, so that the editor scale can be changed
// and the zoom level will still be the same (relative to the editor scale).
zoom = float(p_state["zoom"]) * MAX(1, EDSCALE);
_update_zoom_label();
zoom_widget->set_zoom(zoom);
}
if (state.has("ofs")) {
@ -5594,7 +5541,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
if (state.has("show_zoom_control")) {
// This one is not user-controllable, but instrumentable
zoom_hb->set_visible(state["show_zoom_control"]);
zoom_widget->set_visible(state["show_zoom_control"]);
}
if (state.has("snap_rotation")) {
@ -5770,11 +5717,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
controls_vb = memnew(VBoxContainer);
controls_vb->set_begin(Point2(5, 5));
zoom_hb = memnew(HBoxContainer);
// Bring the zoom percentage closer to the zoom buttons
zoom_hb->add_theme_constant_override("separation", Math::round(-8 * EDSCALE));
controls_vb->add_child(zoom_hb);
viewport = memnew(CanvasItemEditorViewport(p_editor, this));
viewport_scrollable->add_child(viewport);
viewport->set_mouse_filter(MOUSE_FILTER_PASS);
@ -5821,35 +5763,10 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->add_child(controls_vb);
zoom_minus = memnew(Button);
zoom_minus->set_flat(true);
zoom_hb->add_child(zoom_minus);
zoom_minus->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_minus));
zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom Out"), KEY_MASK_CMD | KEY_MINUS));
zoom_minus->set_shortcut_context(this);
zoom_minus->set_focus_mode(FOCUS_NONE);
zoom_reset = memnew(Button);
zoom_reset->set_flat(true);
zoom_hb->add_child(zoom_reset);
zoom_reset->add_theme_constant_override("outline_size", 1);
zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0));
zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1));
zoom_reset->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_reset));
zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset"), KEY_MASK_CMD | KEY_0));
zoom_reset->set_shortcut_context(this);
zoom_reset->set_focus_mode(FOCUS_NONE);
zoom_reset->set_text_align(Button::TextAlign::ALIGN_CENTER);
// Prevent the button's size from changing when the text size changes
zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
zoom_plus = memnew(Button);
zoom_plus->set_flat(true);
zoom_hb->add_child(zoom_plus);
zoom_plus->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_plus));
zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom In"), KEY_MASK_CMD | KEY_EQUAL)); // Usually direct access key for PLUS
zoom_plus->set_shortcut_context(this);
zoom_plus->set_focus_mode(FOCUS_NONE);
zoom_widget = memnew(EditorZoomWidget);
controls_vb->add_child(zoom_widget);
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom));
updating_scroll = false;

View file

@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "editor/editor_zoom_widget.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/label.h"
@ -233,10 +234,6 @@ private:
VScrollBar *v_scroll;
HBoxContainer *hb;
Button *zoom_minus;
Button *zoom_reset;
Button *zoom_plus;
Map<Control *, Timer *> popup_temporarily_timers;
Label *warning_child_of_container;
@ -536,13 +533,9 @@ private:
void _button_toggle_anchor_mode(bool p_status);
VBoxContainer *controls_vb;
HBoxContainer *zoom_hb;
float _get_next_zoom_value(int p_increment_count) const;
EditorZoomWidget *zoom_widget;
void _update_zoom(float p_zoom);
void _zoom_on_position(float p_zoom, Point2 p_position = Point2());
void _update_zoom_label();
void _button_zoom_minus();
void _button_zoom_reset();
void _button_zoom_plus();
void _button_toggle_smart_snap(bool p_status);
void _button_toggle_grid_snap(bool p_status);
void _button_override_camera(bool p_pressed);

File diff suppressed because it is too large Load diff

View file

@ -1,242 +0,0 @@
/*************************************************************************/
/* tile_map_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_MAP_EDITOR_PLUGIN_H
#define TILE_MAP_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/check_box.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/menu_button.h"
class TileMapEditor : public VBoxContainer {
GDCLASS(TileMapEditor, VBoxContainer);
enum Tool {
TOOL_NONE,
TOOL_PAINTING,
TOOL_ERASING,
TOOL_RECTANGLE_PAINT,
TOOL_RECTANGLE_ERASE,
TOOL_LINE_PAINT,
TOOL_LINE_ERASE,
TOOL_SELECTING,
TOOL_BUCKET,
TOOL_PICKING,
TOOL_PASTING
};
enum Options {
OPTION_COPY,
OPTION_ERASE_SELECTION,
OPTION_FIX_INVALID,
OPTION_CUT
};
TileMap *node;
bool manual_autotile;
bool priority_atlastile;
Vector2 manual_position;
EditorNode *editor;
UndoRedo *undo_redo;
Control *canvas_item_editor_viewport;
LineEdit *search_box;
HSlider *size_slider;
ItemList *palette;
ItemList *manual_palette;
Label *info_message;
HBoxContainer *toolbar;
HBoxContainer *toolbar_right;
Label *tile_info;
MenuButton *options;
Button *paint_button;
Button *line_button;
Button *rectangle_button;
Button *bucket_fill_button;
Button *picker_button;
Button *select_button;
Button *flip_horizontal_button;
Button *flip_vertical_button;
Button *rotate_left_button;
Button *rotate_right_button;
Button *clear_transform_button;
CheckBox *manual_button;
CheckBox *priority_button;
Tool tool;
Tool last_tool;
bool selection_active;
bool mouse_over;
bool mouse_down;
bool flip_h;
bool flip_v;
bool transpose;
Point2i autotile_coord;
Point2i rectangle_begin;
Rect2i rectangle;
Point2i over_tile;
bool refocus_over_tile = false;
bool *bucket_cache_visited;
Rect2i bucket_cache_rect;
int bucket_cache_tile;
Vector<Vector2> bucket_cache;
List<Point2i> bucket_queue;
struct CellOp {
int idx = TileMap::INVALID_CELL;
bool xf = false;
bool yf = false;
bool tr = false;
Vector2 ac;
};
Map<Point2i, CellOp> paint_undo;
struct TileData {
Point2i pos;
int cell = TileMap::INVALID_CELL;
bool flip_h = false;
bool flip_v = false;
bool transpose = false;
Point2i autotile_coord;
};
List<TileData> copydata;
Map<Point2i, CellOp> undo_data;
Vector<int> invalid_cell;
void _pick_tile(const Point2 &p_pos);
Vector<Vector2> _bucket_fill(const Point2i &p_start, bool erase = false, bool preview = false);
void _fill_points(const Vector<Vector2> &p_points, const Dictionary &p_op);
void _erase_points(const Vector<Vector2> &p_points);
void _select(const Point2i &p_from, const Point2i &p_to);
void _erase_selection();
void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform);
void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform);
void _clear_bucket_cache();
void _update_copydata();
Vector<int> get_selected_tiles() const;
void set_selected_tiles(Vector<int> p_tile);
void _manual_toggled(bool p_enabled);
void _priority_toggled(bool p_enabled);
void _text_entered(const String &p_text);
void _text_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_ie);
void _update_palette();
void _update_button_tool();
void _button_tool_select(int p_tool);
void _menu_option(int p_option);
void _palette_selected(int index);
void _palette_multi_selected(int index, bool selected);
void _palette_input(const Ref<InputEvent> &p_event);
Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord);
void _start_undo(const String &p_action);
void _finish_undo();
void _create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new);
void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, const Point2i &p_autotile_coord = Point2());
void _canvas_mouse_enter();
void _canvas_mouse_exit();
void _tileset_settings_changed();
void _icon_size_changed(float p_value);
void _clear_transform();
void _flip_horizontal();
void _flip_vertical();
void _rotate(int steps);
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
CellOp _get_op_from_cell(const Point2i &p_pos);
public:
HBoxContainer *get_toolbar() const { return toolbar; }
HBoxContainer *get_toolbar_right() const { return toolbar_right; }
Label *get_tile_info() const { return tile_info; }
bool forward_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(Node *p_tile_map);
TileMapEditor(EditorNode *p_editor);
~TileMapEditor();
};
class TileMapEditorPlugin : public EditorPlugin {
GDCLASS(TileMapEditorPlugin, EditorPlugin);
TileMapEditor *tile_map_editor;
protected:
void _notification(int p_what);
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tile_map_editor->forward_gui_input(p_event); }
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tile_map_editor->forward_canvas_draw_over_viewport(p_overlay); }
virtual String get_name() const override { return "TileMap"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
TileMapEditorPlugin(EditorNode *p_node);
~TileMapEditorPlugin();
};
#endif // TILE_MAP_EDITOR_PLUGIN_H

File diff suppressed because it is too large Load diff

View file

@ -1,298 +0,0 @@
/*************************************************************************/
/* tile_set_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_SET_EDITOR_PLUGIN_H
#define TILE_SET_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "scene/2d/sprite_2d.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/tile_set.h"
#define WORKSPACE_MARGIN Vector2(10, 10)
class TilesetEditorContext;
class TileSetEditor : public HSplitContainer {
friend class TileSetEditorPlugin;
friend class TilesetEditorContext;
GDCLASS(TileSetEditor, HSplitContainer);
enum TextureButtons {
TOOL_TILESET_ADD_TEXTURE,
TOOL_TILESET_REMOVE_TEXTURE,
TOOL_TILESET_CREATE_SCENE,
TOOL_TILESET_MERGE_SCENE,
TOOL_TILESET_MAX
};
enum WorkspaceMode {
WORKSPACE_EDIT,
WORKSPACE_CREATE_SINGLE,
WORKSPACE_CREATE_AUTOTILE,
WORKSPACE_CREATE_ATLAS,
WORKSPACE_MODE_MAX
};
enum EditMode {
EDITMODE_REGION,
EDITMODE_COLLISION,
EDITMODE_OCCLUSION,
EDITMODE_NAVIGATION,
EDITMODE_BITMASK,
EDITMODE_PRIORITY,
EDITMODE_ICON,
EDITMODE_Z_INDEX,
EDITMODE_MAX
};
enum TileSetTools {
SELECT_PREVIOUS,
SELECT_NEXT,
TOOL_SELECT,
BITMASK_COPY,
BITMASK_PASTE,
BITMASK_CLEAR,
SHAPE_NEW_POLYGON,
SHAPE_NEW_RECTANGLE,
SHAPE_TOGGLE_TYPE,
SHAPE_DELETE,
SHAPE_KEEP_INSIDE_TILE,
TOOL_GRID_SNAP,
ZOOM_OUT,
ZOOM_1,
ZOOM_IN,
VISIBLE_INFO,
TOOL_MAX
};
struct SubtileData {
Array collisions;
Ref<OccluderPolygon2D> occlusion_shape;
Ref<NavigationPolygon> navigation_shape;
};
Ref<TileSet> tileset;
TilesetEditorContext *helper;
EditorNode *editor;
UndoRedo *undo_redo;
ConfirmationDialog *cd;
AcceptDialog *err_dialog;
EditorFileDialog *texture_dialog;
ItemList *texture_list;
int option;
Button *tileset_toolbar_buttons[TOOL_TILESET_MAX];
MenuButton *tileset_toolbar_tools;
Map<RID, Ref<Texture2D>> texture_map;
bool creating_shape;
int dragging_point;
bool tile_names_visible;
Vector2 region_from;
Rect2 edited_region;
bool draw_edited_region;
Vector2 edited_shape_coord;
PackedVector2Array current_shape;
Map<Vector2i, SubtileData> current_tile_data;
Map<Vector2, uint32_t> bitmask_map_copy;
Vector2 snap_step;
Vector2 snap_offset;
Vector2 snap_separation;
Ref<Shape2D> edited_collision_shape;
Ref<OccluderPolygon2D> edited_occlusion_shape;
Ref<NavigationPolygon> edited_navigation_shape;
int current_item_index;
Sprite2D *preview;
ScrollContainer *scroll;
Label *empty_message;
Control *workspace_container;
bool draw_handles;
Control *workspace_overlay;
Control *workspace;
Button *tool_workspacemode[WORKSPACE_MODE_MAX];
Button *tool_editmode[EDITMODE_MAX];
HSeparator *separator_editmode;
HBoxContainer *toolbar;
Button *tools[TOOL_MAX];
VSeparator *separator_shape_toggle;
VSeparator *separator_bitmask;
VSeparator *separator_delete;
VSeparator *separator_grid;
SpinBox *spin_priority;
SpinBox *spin_z_index;
WorkspaceMode workspace_mode;
EditMode edit_mode;
int current_tile;
float max_scale;
float min_scale;
float scale_ratio;
void update_texture_list();
void update_texture_list_icon();
void add_texture(Ref<Texture2D> p_texture);
void remove_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_current_texture();
static void _import_node(Node *p_node, Ref<TileSet> p_library);
static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge);
void _undo_redo_import_scene(Node *p_scene, bool p_merge);
bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const;
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
protected:
static void _bind_methods();
void _notification(int p_what);
public:
void edit(const Ref<TileSet> &p_tileset);
static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true);
TileSetEditor(EditorNode *p_editor);
~TileSetEditor();
private:
void _on_tileset_toolbar_button_pressed(int p_index);
void _on_tileset_toolbar_confirm();
void _on_texture_list_selected(int p_index);
void _on_textures_added(const PackedStringArray &p_paths);
void _on_edit_mode_changed(int p_edit_mode);
void _on_workspace_mode_changed(int p_workspace_mode);
void _on_workspace_overlay_draw();
void _on_workspace_draw();
void _on_workspace_process();
void _on_scroll_container_input(const Ref<InputEvent> &p_event);
void _on_workspace_input(const Ref<InputEvent> &p_ie);
void _on_tool_clicked(int p_tool);
void _on_priority_changed(float val);
void _on_z_index_changed(float val);
void _on_grid_snap_toggled(bool p_val);
Vector<Vector2> _get_collision_shape_points(const Ref<Shape2D> &p_shape);
Vector<Vector2> _get_edited_shape_points();
void _set_edited_shape_points(const Vector<Vector2> &points);
void _update_tile_data();
void _update_toggle_shape_button();
void _select_next_tile();
void _select_previous_tile();
Array _get_tiles_in_current_texture(bool sorted = false);
bool _sort_tiles(Variant p_a, Variant p_b);
void _select_next_subtile();
void _select_previous_subtile();
void _select_next_shape();
void _select_previous_shape();
void _set_edited_collision_shape(const Ref<Shape2D> &p_shape);
void _set_snap_step(Vector2 p_val);
void _set_snap_off(Vector2 p_val);
void _set_snap_sep(Vector2 p_val);
void _validate_current_tile_id();
void _select_edited_shape_coord();
void _undo_tile_removal(int p_id);
void _zoom_in();
void _zoom_out();
void _zoom_reset();
void draw_highlight_current_tile();
void draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>());
void draw_tile_subdivision(int p_id, Color p_color) const;
void draw_edited_region_subdivision() const;
void draw_grid_snap();
void draw_polygon_shapes();
void close_shape(const Vector2 &shape_anchor);
void select_coord(const Vector2 &coord);
Vector2 snap_point(const Vector2 &point);
void update_workspace_tile_mode();
void update_workspace_minsize();
void update_edited_region(const Vector2 &end_point);
int get_grabbed_point(const Vector2 &p_mouse_pos, real_t grab_threshold);
bool is_within_grabbing_distance_of_first_point(const Vector2 &p_pos, real_t p_grab_threshold);
int get_current_tile() const;
void set_current_tile(int p_id);
};
class TilesetEditorContext : public Object {
friend class TileSetEditor;
GDCLASS(TilesetEditorContext, Object);
Ref<TileSet> tileset;
TileSetEditor *tileset_editor;
bool snap_options_visible;
public:
bool _hide_script_from_inspector() { return true; }
void set_tileset(const Ref<TileSet> &p_tileset);
private:
void set_snap_options_visible(bool p_visible);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
TilesetEditorContext(TileSetEditor *p_tileset_editor);
};
class TileSetEditorPlugin : public EditorPlugin {
GDCLASS(TileSetEditorPlugin, EditorPlugin);
TileSetEditor *tileset_editor;
Button *tileset_editor_button;
EditorNode *editor;
public:
virtual String get_name() const override { return "TileSet"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_node) override;
virtual bool handles(Object *p_node) const override;
virtual void make_visible(bool p_visible) override;
void set_state(const Dictionary &p_state) override;
Dictionary get_state() const override;
TileSetEditorPlugin(EditorNode *p_node);
};
#endif // TILE_SET_EDITOR_PLUGIN_H

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

View file

@ -0,0 +1,649 @@
/*************************************************************************/
/* tile_atlas_view.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tile_atlas_view.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/center_container.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/gui/texture_rect.h"
#include "editor/editor_scale.h"
void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) {
bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
// Zoom out
zoom_widget->set_zoom_by_increments(-2);
emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
_update_zoom(zoom_widget->get_zoom(), true);
accept_event();
}
if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
// Zoom in
zoom_widget->set_zoom_by_increments(2);
emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
_update_zoom(zoom_widget->get_zoom(), true);
accept_event();
}
}
}
Size2i TileAtlasView::_compute_base_tiles_control_size() {
// Update the texture.
Vector2i size;
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
size = texture->get_size();
}
// Extend the size to all existing tiles.
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
grid_size = grid_size.max(tile_id + Vector2i(1, 1));
}
size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins());
return size;
}
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
Vector2i size;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Vector2i line_size;
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
bool transposed = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id))->get_transpose();
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
}
size.x = MAX(size.x, line_size.x);
size.y += line_size.y;
}
return size;
}
void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2i p_scroll) {
// Compute the minimum sizes.
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * p_zoom);
Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * p_zoom);
// Set the texture for the base tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
// Set the scales.
if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
base_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom));
} else {
base_tiles_drawing_root->set_scale(Vector2(1, 1));
}
if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
alternative_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom));
} else {
alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
}
// Update the margin container's margins.
const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
for (int i = 0; i < 4; i++) {
margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * p_zoom);
}
// Update the backgrounds.
background_left->update();
background_right->update();
if (p_scroll != Vector2i(-1, -1)) {
scroll_container->set_h_scroll(p_scroll.x);
scroll_container->set_v_scroll(p_scroll.y);
}
// Zoom on the position.
if (previous_zoom != p_zoom) {
// TODO: solve this.
// There is however an issue with scrollcainter preventing this, as it seems
// that the scrollbars are not updated right aways after its children update.
// Compute point on previous area.
/*Vector2 max = Vector2(scroll_container->get_h_scrollbar()->get_max(), scroll_container->get_v_scrollbar()->get_max());
Vector2 min = Vector2(scroll_container->get_h_scrollbar()->get_min(), scroll_container->get_v_scrollbar()->get_min());
Vector2 value = Vector2(scroll_container->get_h_scrollbar()->get_value(), scroll_container->get_v_scrollbar()->get_value());
Vector2 old_max = max * previous_zoom / p_zoom;
Vector2 max_pixel_change = max - old_max;
Vector2 ratio = ((value + scroll_container->get_local_mouse_position()) / old_max).max(Vector2()).min(Vector2(1,1));
Vector2 offset = max_pixel_change * ratio;
print_line("--- ZOOMED ---");
print_line(vformat("max: %s", max));
print_line(vformat("min: %s", min));
print_line(vformat("value: %s", value));
print_line(vformat("size: %s", scroll_container->get_size()));
print_line(vformat("mouse_pos: %s", scroll_container->get_local_mouse_position()));
print_line(vformat("ratio: %s", ratio));
print_line(vformat("max_pixel_change: %s", max_pixel_change));
print_line(vformat("offset: %s", offset));
print_line(vformat("value before: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())));
scroll_container->set_h_scroll(10000);//scroll_container->get_h_scroll()+offset.x);
scroll_container->set_v_scroll(10000);//scroll_container->get_v_scroll()+offset.y);
print_line(vformat("value after: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())));
*/
previous_zoom = p_zoom;
}
}
void TileAtlasView::_scroll_changed() {
emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
}
void TileAtlasView::_zoom_widget_changed() {
_update_zoom(zoom_widget->get_zoom());
emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
}
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
base_tiles_root_control->set_tooltip("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
coords = tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
base_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
}
}
}
}
void TileAtlasView::_draw_base_tiles() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
// Draw the texture, square by square.
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
}
}
}
// Draw the texture around the grid.
Rect2i rect;
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Bottom
rect.position = Vector2i(0, margins.y + (grid_size.y * texture_region_size.y));
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Left
rect.position = Vector2i(0, margins.y);
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Right.
rect.position = Vector2i(margins.x + (grid_size.x * texture_region_size.x), margins.y);
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
// Draw actual tiles, using their properties (modulation, etc...)
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
// Update the y to max value.
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.
TileSetAtlasPluginRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
}
}
}
void TileAtlasView::_draw_base_tiles_texture_grid() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw each tile texture region.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
if (base_tile_coords == Vector2i(x, y)) {
// Draw existing tile.
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
}
} else {
// Draw the grid.
base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
}
}
}
}
}
void TileAtlasView::_draw_base_tiles_dark() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
// Draw each tile texture region.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
if (base_tile_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
// Draw the grid.
base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
}
}
}
}
}
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
Vector2i tile_shape_size = tile_set->get_tile_size();
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0);
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset;
// Draw only if the tile shape fits in the texture region
tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), Color(1.0, 0.5, 0.2, 0.8));
}
}
void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
alternative_tiles_root_control->set_tooltip("");
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
Vector2i coords = Vector2i(coords3.x, coords3.y);
int alternative_id = coords3.z;
if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
alternative_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
}
}
}
void TileAtlasView::_draw_alternatives() {
// Draw the alternative tiles.
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2 current_pos;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id));
bool transposed = tile_data->get_transpose();
// Update the y to max value.
Vector2i offset_pos = current_pos;
if (transposed) {
offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region.size.x);
} else {
offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
y_increment = MAX(y_increment, texture_region.size.y);
}
// Draw the tile.
TileSetAtlasPluginRendering::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;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;
}
}
}
}
void TileAtlasView::_draw_background_left() {
Ref<Texture2D> texture = get_theme_icon("Checkerboard", "EditorIcons");
background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
background_left->draw_texture_rect(texture, Rect2(Vector2(), background_left->get_size()), true);
}
void TileAtlasView::_draw_background_right() {
Ref<Texture2D> texture = get_theme_icon("Checkerboard", "EditorIcons");
background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
background_right->draw_texture_rect(texture, Rect2(Vector2(), background_right->get_size()), true);
}
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set);
ERR_FAIL_COND(!p_tile_set_atlas_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
tile_set = p_tile_set;
tile_set_atlas_source = p_tile_set_atlas_source;
source_id = p_source_id;
// Show or hide the view.
bool valid = tile_set_atlas_source->get_texture().is_valid();
hbox->set_visible(valid);
missing_source_label->set_visible(!valid);
// Update the rect cache.
_update_alternative_tiles_rect_cache();
// Update everything.
_update_zoom(zoom_widget->get_zoom());
// Change children control size.
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
for (int i = 0; i < base_tiles_drawing_root->get_child_count(); i++) {
Control *control = Object::cast_to<Control>(base_tiles_drawing_root->get_child(i));
if (control) {
control->set_size(base_tiles_control_size);
}
}
Size2i alternative_control_size = _compute_alternative_tiles_control_size();
for (int i = 0; i < alternative_tiles_drawing_root->get_child_count(); i++) {
Control *control = Object::cast_to<Control>(alternative_tiles_drawing_root->get_child(i));
if (control) {
control->set_size(alternative_control_size);
}
}
// Update.
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
}
float TileAtlasView::get_zoom() const {
return zoom_widget->get_zoom();
};
void TileAtlasView::set_transform(float p_zoom, Vector2i p_scroll) {
zoom_widget->set_zoom(p_zoom);
_update_zoom(zoom_widget->get_zoom(), false, p_scroll);
};
void TileAtlasView::set_padding(Side p_side, int p_padding) {
ERR_FAIL_COND(p_padding < 0);
margin_container_paddings[p_side] = p_padding;
}
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
// Compute index in atlas
Vector2 pos = p_pos - margins;
Vector2i ret = (pos / (texture_region_size + separation)).floor();
return ret;
}
return TileSetAtlasSource::INVALID_ATLAS_COORDS;
}
void TileAtlasView::_update_alternative_tiles_rect_cache() {
alternative_tiles_rect_cache.clear();
Rect2i current;
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
int line_height = 0;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id));
bool transposed = tile_data->get_transpose();
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
// Update the rect.
if (!alternative_tiles_rect_cache.has(tile_id)) {
alternative_tiles_rect_cache[tile_id] = Map<int, Rect2i>();
}
alternative_tiles_rect_cache[tile_id][alternative_id] = current;
current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
}
current.position.x = 0;
current.position.y += line_height;
}
}
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
for (Map<Vector2, Map<int, Rect2i>>::Element *E_coords = alternative_tiles_rect_cache.front(); E_coords; E_coords = E_coords->next()) {
for (Map<int, Rect2i>::Element *E_alternative = E_coords->value().front(); E_alternative; E_alternative = E_alternative->next()) {
if (E_alternative->value().has_point(p_pos)) {
return Vector3i(E_coords->key().x, E_coords->key().y, E_alternative->key());
}
}
}
return Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
}
Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache[p_coords].has(p_alternative_tile), Rect2i(), vformat("No cached rect for tile coords:%s alternative_id:%d", p_coords, p_alternative_tile));
return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
}
void TileAtlasView::update() {
scroll_container->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
}
void TileAtlasView::_bind_methods() {
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
}
TileAtlasView::TileAtlasView() {
Panel *panel_container = memnew(Panel);
panel_container->set_h_size_flags(SIZE_EXPAND_FILL);
panel_container->set_v_size_flags(SIZE_EXPAND_FILL);
panel_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
add_child(panel_container);
//Scrolling
scroll_container = memnew(ScrollContainer);
scroll_container->get_h_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1));
scroll_container->get_v_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1));
panel_container->add_child(scroll_container);
scroll_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
zoom_widget = memnew(EditorZoomWidget);
add_child(zoom_widget);
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
CenterContainer *center_container = memnew(CenterContainer);
center_container->set_h_size_flags(SIZE_EXPAND_FILL);
center_container->set_v_size_flags(SIZE_EXPAND_FILL);
center_container->connect("gui_input", callable_mp(this, &TileAtlasView::_gui_input));
scroll_container->add_child(center_container);
missing_source_label = memnew(Label);
missing_source_label->set_text(TTR("No atlas source with a valid texture selected."));
center_container->add_child(missing_source_label);
margin_container = memnew(MarginContainer);
center_container->add_child(margin_container);
hbox = memnew(HBoxContainer);
hbox->add_theme_constant_override("separation", 10);
hbox->hide();
margin_container->add_child(hbox);
VBoxContainer *left_vbox = memnew(VBoxContainer);
hbox->add_child(left_vbox);
VBoxContainer *right_vbox = memnew(VBoxContainer);
hbox->add_child(right_vbox);
// Base tiles.
Label *base_tile_label = memnew(Label);
base_tile_label->set_text(TTR("Base tiles"));
base_tile_label->set_align(Label::ALIGN_CENTER);
left_vbox->add_child(base_tile_label);
base_tiles_root_control = memnew(Control);
base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
left_vbox->add_child(base_tiles_root_control);
background_left = memnew(Control);
background_left->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
base_tiles_root_control->add_child(background_left);
base_tiles_drawing_root = memnew(Control);
base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_root_control->add_child(base_tiles_drawing_root);
base_tiles_draw = memnew(Control);
base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles));
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->add_child(base_tiles_draw);
base_tiles_texture_grid = memnew(Control);
base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
base_tiles_shape_grid = memnew(Control);
base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
base_tiles_dark = memnew(Control);
base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_drawing_root->add_child(base_tiles_dark);
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_text(TTR("Alternative tiles"));
alternative_tiles_label->set_align(Label::ALIGN_CENTER);
right_vbox->add_child(alternative_tiles_label);
alternative_tiles_root_control = memnew(Control);
alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
right_vbox->add_child(alternative_tiles_root_control);
background_right = memnew(Control);
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right));
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_root_control->add_child(background_right);
alternative_tiles_drawing_root = memnew(Control);
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
alternatives_draw = memnew(Control);
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_drawing_root->add_child(alternatives_draw);
}

View file

@ -0,0 +1,153 @@
/*************************************************************************/
/* tile_atlas_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_ATLAS_VIEW_H
#define TILE_ATLAS_VIEW_H
#include "editor/editor_zoom_widget.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/tile_set.h"
class TileAtlasView : public Control {
GDCLASS(TileAtlasView, Control);
private:
TileSet *tile_set;
TileSetAtlasSource *tile_set_atlas_source;
int source_id = -1;
float previous_zoom = 1.0;
EditorZoomWidget *zoom_widget;
void _zoom_widget_changed();
void _scroll_changed();
void _update_zoom(float p_zoom, bool p_zoom_on_mouse_pos = false, Vector2i p_scroll = Vector2i(-1, -1));
void _gui_input(const Ref<InputEvent> &p_event);
Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
ScrollContainer *scroll_container;
MarginContainer *margin_container;
int margin_container_paddings[4] = { 0, 0, 0, 0 };
HBoxContainer *hbox;
Label *missing_source_label;
// Background
Control *background_left;
void _draw_background_left();
Control *background_right;
void _draw_background_right();
// Left side.
Control *base_tiles_root_control;
void _base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *base_tiles_drawing_root;
Control *base_tiles_draw;
void _draw_base_tiles();
Control *base_tiles_texture_grid;
void _draw_base_tiles_texture_grid();
Control *base_tiles_shape_grid;
void _draw_base_tiles_shape_grid();
Control *base_tiles_dark;
void _draw_base_tiles_dark();
Size2i _compute_base_tiles_control_size();
// Right side.
Control *alternative_tiles_root_control;
void _alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
Control *alternative_tiles_drawing_root;
Control *alternatives_draw;
void _draw_alternatives();
Size2i _compute_alternative_tiles_control_size();
protected:
static void _bind_methods();
public:
// Global.
void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
ScrollContainer *get_scroll_container() { return scroll_container; };
float get_zoom() const;
void set_transform(float p_zoom, Vector2i p_scroll);
void set_padding(Side p_side, int p_padding);
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
base_tiles_drawing_root->add_child(p_control);
} else {
base_tiles_root_control->add_child(p_control);
}
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
// Right side.
Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const;
Rect2i get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile);
void add_control_over_alternative_tiles(Control *p_control, bool scaled = true) {
if (scaled) {
alternative_tiles_drawing_root->add_child(p_control);
} else {
alternative_tiles_root_control->add_child(p_control);
}
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
// Update everything.
void update();
TileAtlasView();
};
#endif // TILE_ATLAS_VIEW

View file

@ -0,0 +1,218 @@
/*************************************************************************/
/* tile_data_editors.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tile_data_editors.h"
#include "tile_set_editor.h"
TileData *TileDataEditor::_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile) {
ERR_FAIL_COND_V(!p_tile_set, nullptr);
ERR_FAIL_COND_V(!p_tile_set->has_source(p_atlas_source_id), nullptr);
TileData *td = nullptr;
TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
ERR_FAIL_COND_V(!atlas_source->has_tile(p_atlas_coords), nullptr);
ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_atlas_coords, p_alternative_tile), nullptr);
td = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
}
return td;
}
void TileDataEditor::edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
}
void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
bool valid;
Variant value = tile_data->get(p_property, &valid);
if (!valid) {
return;
}
ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I);
Vector2i tile_set_tile_size = p_tile_set->get_tile_size();
Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size);
p_tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), Color(1.0, 0.0, 0.0));
}
void TileDataIntegerEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
bool valid;
Variant value = tile_data->get(p_property, &valid);
if (!valid) {
return;
}
ERR_FAIL_COND(value.get_type() != Variant::INT);
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts");
int height = font->get_height();
int width = 200;
p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%d", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1));
}
void TileDataFloatEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
bool valid;
Variant value = tile_data->get(p_property, &valid);
if (!valid) {
return;
}
ERR_FAIL_COND(value.get_type() != Variant::FLOAT);
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts");
int height = font->get_height();
int width = 200;
p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%.2f", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1));
}
void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
bool valid;
Variant value = tile_data->get(p_property, &valid);
if (!valid) {
return;
}
ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2);
Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons");
p_canvas_item->draw_texture(position_icon, p_transform.get_origin() + Vector2(value) - position_icon->get_size() / 2);
}
void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Vector<String> components = String(p_property).split("/", true);
if (components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
int occlusion_layer = components[0].trim_prefix("occlusion_layer_").to_int();
if (occlusion_layer >= 0 && occlusion_layer < p_tile_set->get_occlusion_layers_count()) {
// Draw all shapes.
Vector<Color> debug_occlusion_color;
debug_occlusion_color.push_back(Color(0.5, 0, 0, 0.6));
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
Ref<OccluderPolygon2D> occluder = tile_data->get_occluder(occlusion_layer);
if (occluder.is_valid() && occluder->get_polygon().size() >= 3) {
p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color);
}
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
}
}
void TileDataCollisionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Vector<String> components = String(p_property).split("/", true);
if (components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
int physics_layer = components[0].trim_prefix("physics_layer_").to_int();
if (physics_layer >= 0 && physics_layer < p_tile_set->get_physics_layers_count()) {
// Draw all shapes.
Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color();
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
for (int i = 0; i < tile_data->get_collision_shapes_count(physics_layer); i++) {
Ref<Shape2D> shape = tile_data->get_collision_shape_shape(physics_layer, i);
if (shape.is_valid()) {
shape->draw(p_canvas_item->get_canvas_item(), debug_collision_color);
}
}
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
}
}
void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Vector<String> components = String(p_property).split("/", true);
if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") {
TileSetAtlasPluginTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data);
}
}
void TileDataNavigationPolygonEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Vector<String> components = String(p_property).split("/", true);
if (components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
int navigation_layer = components[0].trim_prefix("navigation_layer_").to_int();
if (navigation_layer >= 0 && navigation_layer < p_tile_set->get_navigation_layers_count()) {
// Draw all shapes.
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer);
if (navigation_polygon.is_valid()) {
Vector<Vector2> verts = navigation_polygon->get_vertices();
if (verts.size() < 3) {
return;
}
Color color = p_canvas_item->get_tree()->get_debug_navigation_color();
RandomPCG rand;
for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) {
// An array of vertices for this polygon.
Vector<int> polygon = navigation_polygon->get_polygon(i);
Vector<Vector2> vertices;
vertices.resize(polygon.size());
for (int j = 0; j < polygon.size(); j++) {
ERR_FAIL_INDEX(polygon[j], verts.size());
vertices.write[j] = verts[polygon[j]];
}
// Generate the polygon color, slightly randomly modified from the settings one.
Color random_variation_color;
random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1);
random_variation_color.a = color.a;
Vector<Color> colors;
colors.push_back(random_variation_color);
RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors);
}
}
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
}
}

View file

@ -0,0 +1,110 @@
/*************************************************************************/
/* tile_data_editors.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_DATA_EDITORS_H
#define TILE_DATA_EDITORS_H
#include "scene/gui/control.h"
#include "scene/resources/tile_set.h"
class TileDataEditor : public Control {
GDCLASS(TileDataEditor, Control);
protected:
TileData *tile_data;
String property;
TileData *_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile);
public:
// Edits a TileData property.
void edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property);
// Used to draw the value over a tile.
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property){};
};
class TileDataTextureOffsetEditor : public TileDataEditor {
GDCLASS(TileDataTextureOffsetEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataIntegerEditor : public TileDataEditor {
GDCLASS(TileDataIntegerEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataFloatEditor : public TileDataEditor {
GDCLASS(TileDataFloatEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataPositionEditor : public TileDataEditor {
GDCLASS(TileDataPositionEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataOcclusionShapeEditor : public TileDataEditor {
GDCLASS(TileDataOcclusionShapeEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataCollisionShapeEditor : public TileDataEditor {
GDCLASS(TileDataCollisionShapeEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataTerrainsEditor : public TileDataEditor {
GDCLASS(TileDataTerrainsEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
class TileDataNavigationPolygonEditor : public TileDataEditor {
GDCLASS(TileDataNavigationPolygonEditor, TileDataEditor);
public:
virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
};
#endif // TILE_DATA_EDITORS_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
/*************************************************************************/
/* tile_map_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_MAP_EDITOR_H
#define TILE_MAP_EDITOR_H
#include "tile_atlas_view.h"
#include "core/typedefs.h"
#include "editor/editor_node.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
#include "scene/gui/tabs.h"
class TileMapEditorPlugin : public VBoxContainer {
public:
virtual Control *get_toolbar() const {
return memnew(Control);
};
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){};
};
class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin);
private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
ObjectID tile_map_id;
virtual void edit(ObjectID p_tile_map_id) override;
// Toolbar.
HBoxContainer *toolbar;
Ref<ButtonGroup> tool_buttons_group;
Button *select_tool_button;
Button *paint_tool_button;
Button *line_tool_button;
Button *rect_tool_button;
Button *bucket_tool_button;
Button *picker_button;
HBoxContainer *tools_settings;
VSeparator *tools_settings_vsep;
Button *erase_button;
CheckBox *bucket_continuous_checkbox;
VSeparator *tools_settings_vsep_2;
CheckBox *random_tile_checkbox;
float scattering = 0.0;
Label *scatter_label;
SpinBox *scatter_spinbox;
void _on_random_tile_checkbox_toggled(bool p_pressed);
void _on_scattering_spinbox_changed(double p_value);
void _update_toolbar();
// Tilemap editing.
bool has_mouse = false;
void _mouse_exited_viewport();
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_SELECT,
DRAG_TYPE_MOVE,
DRAG_TYPE_PAINT,
DRAG_TYPE_LINE,
DRAG_TYPE_RECT,
DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
DRAG_TYPE_CLIPBOARD_PASTE,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos);
Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_mouse_pos, Vector2i p_end_mouse_pos);
Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
void _stop_dragging();
// Selection system.
Set<Vector2i> tile_map_selection;
TileMapPattern *tile_map_clipboard = memnew(TileMapPattern);
TileMapPattern *selection_pattern = memnew(TileMapPattern);
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
TypedArray<Vector2i> _get_tile_map_selection() const;
Set<TileMapCell> tile_set_selection;
void _update_selection_pattern_from_tilemap_selection();
void _update_selection_pattern_from_tileset_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
// Bottom panel.
bool tile_set_dragging_selection = false;
Vector2i tile_set_drag_start_mouse_pos;
Label *missing_source_label;
HSplitContainer *atlas_sources_split_container;
ItemList *sources_list;
TileAtlasView *tile_atlas_view;
Ref<Texture2D> missing_texture_texture;
void _update_tile_set_sources_list();
void _update_atlas_view();
void _update_bottom_panel();
TileMapCell hovered_tile;
Control *tile_atlas_control;
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_control_draw();
Control *alternative_tiles_control;
void _tile_alternatives_control_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
// Update callback
virtual void tile_set_changed() override;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
virtual Control *get_toolbar() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTilesPlugin();
~TileMapEditorTilesPlugin();
};
class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin {
GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin);
private:
UndoRedo *undo_redo = EditorNode::get_undo_redo();
ObjectID tile_map_id;
virtual void edit(ObjectID p_tile_map_id) override;
// Toolbar.
HBoxContainer *toolbar;
Ref<ButtonGroup> tool_buttons_group;
Button *paint_tool_button;
HBoxContainer *tools_settings;
VSeparator *tools_settings_vsep;
Button *picker_button;
Button *erase_button;
void _update_toolbar();
// TileMap editing.
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
DRAG_TYPE_PICK,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
// Painting
class Constraint {
private:
const TileMap *tile_map;
Vector2i base_cell_coords = Vector2i();
int bit = -1;
int terrain = -1;
public:
// TODO implement difference operator.
bool operator<(const Constraint &p_other) const {
if (base_cell_coords == p_other.base_cell_coords) {
return bit < p_other.bit;
}
return base_cell_coords < p_other.base_cell_coords;
}
String to_string() const {
return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
}
Vector2i get_base_cell_coords() const {
return base_cell_coords;
}
Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
void set_terrain(int p_terrain) {
terrain = p_terrain;
}
int get_terrain() const {
return terrain;
}
Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
Constraint() {}
};
typedef Array TerrainsTilePattern;
Set<TerrainsTilePattern> _get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const;
Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const;
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;
// 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;
Map<TileMapCell, TileData *> terrain_tiles;
LocalVector<TileSet::CellNeighbor> tile_sides;
// Bottom panel.
Tree *terrains_tree;
ItemList *terrains_tile_list;
// Update functions.
void _update_terrains_cache();
void _update_terrains_tree();
void _update_tiles_list();
// Update callback
virtual void tile_set_changed() override;
protected:
void _notification(int p_what);
// static void _bind_methods();
public:
virtual Control *get_toolbar() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
//virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTerrainsPlugin();
~TileMapEditorTerrainsPlugin();
};
class TileMapEditor : public VBoxContainer {
GDCLASS(TileMapEditor, VBoxContainer);
private:
bool tileset_changed_needs_update = false;
ObjectID tile_map_id;
// Vector to keep plugins.
Vector<TileMapEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
HBoxContainer *tilemap_toolbar;
// Bottom panel
Label *missing_tileset_label;
Tabs *tabs;
void _update_bottom_panel();
// TileMap
Ref<Texture2D> missing_tile_texture;
Ref<Texture2D> warning_pattern_texture;
// CallBack
void _tile_map_changed();
void _tab_changed(int p_tab_changed);
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);
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMap *p_tile_map);
Control *get_toolbar() { return tilemap_toolbar; };
TileMapEditor();
~TileMapEditor();
// Static functions.
static Vector<Vector2i> get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell);
};
#endif // TILE_MAP_EDITOR_PLUGIN_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,260 @@
/*************************************************************************/
/* tile_set_atlas_source_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_SET_ATLAS_SOURCE_EDITOR_H
#define TILE_SET_ATLAS_SOURCE_EDITOR_H
#include "tile_atlas_view.h"
#include "editor/editor_node.h"
#include "scene/gui/split_container.h"
#include "scene/resources/tile_set.h"
class TileSet;
class TileSetAtlasSourceEditor : public HBoxContainer {
GDCLASS(TileSetAtlasSourceEditor, HBoxContainer);
private:
// A class to store which tiles are selected.
struct TileSelection {
Vector2i tile = TileSetAtlasSource::INVALID_ATLAS_COORDS;
int alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
bool operator<(const TileSelection &p_other) const {
if (tile == p_other.tile) {
return alternative < p_other.alternative;
} else {
return tile < p_other.tile;
}
}
};
// -- Proxy object for an atlas source, needed by the inspector --
class TileSetAtlasSourceProxyObject : public Object {
GDCLASS(TileSetAtlasSourceProxyObject, Object);
private:
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int source_id = -1;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
void set_id(int p_id);
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
};
// -- Proxy object for a tile, needed by the inspector --
class TileProxyObject : public Object {
GDCLASS(TileProxyObject, Object);
private:
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int source_id;
Set<TileSelection> tiles = Set<TileSelection>();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
// Update the proxyed object.
void edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id = -1, Set<TileSelection> p_tiles = Set<TileSelection>());
TileProxyObject(TileSetAtlasSourceEditor *p_tiles_editor_source_tab) {
tiles_set_atlas_source_editor = p_tiles_editor_source_tab;
}
};
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int tile_set_atlas_source_id = -1;
UndoRedo *undo_redo = EditorNode::get_undo_redo();
bool tile_set_atlas_source_changed_needs_update = false;
// -- Inspector --
TileProxyObject *tile_proxy_object;
Label *tile_inspector_label;
EditorInspector *tile_inspector;
String selected_property;
void _inspector_property_selected(String p_property);
TileSetAtlasSourceProxyObject *atlas_source_proxy_object;
Label *atlas_source_inspector_label;
EditorInspector *atlas_source_inspector;
// -- Atlas view --
HBoxContainer *toolbox;
Label *tile_atlas_view_missing_source_label;
TileAtlasView *tile_atlas_view;
// Dragging
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_CREATE_TILES,
DRAG_TYPE_CREATE_TILES_USING_RECT,
DRAG_TYPE_CREATE_BIG_TILE,
DRAG_TYPE_REMOVE_TILES,
DRAG_TYPE_REMOVE_TILES_USING_RECT,
DRAG_TYPE_MOVE_TILE,
DRAG_TYPE_RECT_SELECT,
// Warning: keep in this order.
DRAG_TYPE_RESIZE_TOP_LEFT,
DRAG_TYPE_RESIZE_TOP,
DRAG_TYPE_RESIZE_TOP_RIGHT,
DRAG_TYPE_RESIZE_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM_RIGHT,
DRAG_TYPE_RESIZE_BOTTOM,
DRAG_TYPE_RESIZE_BOTTOM_LEFT,
DRAG_TYPE_RESIZE_LEFT,
};
DragType drag_type = DRAG_TYPE_NONE;
Vector2i drag_start_mouse_pos;
Vector2i drag_last_mouse_pos;
Vector2i drag_current_tile;
Rect2i drag_start_tile_shape;
Set<Vector2i> drag_modified_tiles;
void _end_dragging();
Map<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas);
// Popup functions.
enum MenuOptions {
TILE_CREATE,
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
};
Vector2i menu_option_coords;
int menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
void _menu_option(int p_option);
// Tool buttons.
Ref<ButtonGroup> tools_button_group;
Button *tool_select_button;
Button *tool_add_remove_button;
Button *tool_add_remove_rect_button;
Label *tool_tile_id_label;
HBoxContainer *tool_settings;
VSeparator *tool_settings_vsep;
Button *tools_settings_erase_button;
MenuButton *tool_advanced_menu_buttom;
// Selection.
Set<TileSelection> selection;
void _set_selection_from_array(Array p_selection);
Array _get_selection_as_array();
// A control on the tile atlas to draw and handle input events.
Vector2i hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
PopupMenu *base_tile_popup_menu;
PopupMenu *empty_base_tile_popup_menu;
Ref<Texture2D> resize_handle;
Ref<Texture2D> resize_handle_disabled;
Control *tile_atlas_control;
Control *tile_atlas_control_unscaled;
void _tile_atlas_control_draw();
void _tile_atlas_control_unscaled_draw();
void _tile_atlas_control_mouse_exited();
void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
void _tile_atlas_view_transform_changed();
// A control over the alternative tiles.
Vector3i hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
PopupMenu *alternative_tile_popup_menu;
Control *alternative_tiles_control;
Control *alternative_tiles_control_unscaled;
void _tile_alternatives_control_draw();
void _tile_alternatives_control_unscaled_draw();
void _tile_alternatives_control_mouse_exited();
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
// -- Update functions --
void _update_tile_id_label();
void _update_source_inspector();
void _update_fix_selected_and_hovered_tiles();
void _update_tile_inspector();
void _update_manage_tile_properties_button();
void _update_atlas_view();
void _update_toolbar();
// -- input events --
void _unhandled_key_input(const Ref<InputEvent> &p_event);
// -- Misc --
void _auto_create_tiles();
void _auto_remove_tiles();
AcceptDialog *confirm_auto_create_tiles;
void _tile_set_atlas_source_changed();
void _atlas_source_proxy_object_changed(String p_what);
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);
static void _bind_methods();
public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_source();
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
};
#endif // TILE_SET_ATLAS_SOURCE_EDITOR_H

View file

@ -0,0 +1,520 @@
/*************************************************************************/
/* tile_set_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tile_set_editor.h"
#include "tile_data_editors.h"
#include "tiles_editor_plugin.h"
#include "editor/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
#include "scene/gui/tab_container.h"
TileSetEditor *TileSetEditor::singleton = nullptr;
void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
ERR_FAIL_COND(!tile_set.is_valid());
if (!can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
if (p_from == sources_list) {
// Handle dropping a texture in the list of atlas resources.
int source_id = -1;
int added = 0;
Dictionary d = p_data;
Vector<String> files = d["files"];
for (int i = 0; i < files.size(); i++) {
Ref<Texture2D> resource = ResourceLoader::load(files[i]);
if (resource.is_valid()) {
// Retrieve the id for the next created source.
source_id = tile_set->get_next_source_id();
// Actually create the new source.
Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
atlas_source->set_texture(resource);
undo_redo->create_action(TTR("Add a new atlas source"));
undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
undo_redo->commit_action();
added += 1;
}
}
if (added == 1) {
tile_set_atlas_source_editor->init_source();
}
// Update the selected source (thus trigerring an update).
_update_atlas_sources_list(source_id);
}
}
bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
ERR_FAIL_COND_V(!tile_set.is_valid(), false);
if (p_from == sources_list) {
Dictionary d = p_data;
if (!d.has("type")) {
return false;
}
// Check if we have a Texture2D.
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
if (files.size() == 0) {
return false;
}
for (int i = 0; i < files.size(); i++) {
String file = files[i];
String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
if (!ClassDB::is_parent_class(ftype, "Texture2D")) {
return false;
}
}
return true;
}
}
return false;
}
void TileSetEditor::_update_atlas_sources_list(int force_selected_id) {
ERR_FAIL_COND(!tile_set.is_valid());
// Get the previously selected id.
int old_selected = -1;
if (sources_list->get_current() >= 0) {
int source_id = sources_list->get_item_metadata(sources_list->get_current());
if (tile_set->has_source(source_id)) {
old_selected = source_id;
}
}
int to_select = -1;
if (force_selected_id >= 0) {
to_select = force_selected_id;
} else if (old_selected >= 0) {
to_select = old_selected;
}
// Clear the list.
sources_list->clear();
// Update the atlas sources.
for (int i = 0; i < tile_set->get_source_count(); i++) {
int source_id = tile_set->get_source_id(i);
// TODO: handle with virtual functions
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
Ref<Texture2D> texture = atlas_source->get_texture();
if (texture.is_valid()) {
sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture);
} else {
sources_list->add_item(vformat("No texture atlas source - (id:%d)", source_id), missing_texture_texture);
}
} else {
sources_list->add_item(vformat("Unknown type source - (id:%d)", source_id), missing_texture_texture);
}
sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
}
// Set again the current selected item if needed.
if (to_select >= 0) {
for (int i = 0; i < sources_list->get_item_count(); i++) {
if ((int)sources_list->get_item_metadata(i) == to_select) {
sources_list->set_current(i);
if (old_selected != to_select) {
sources_list->emit_signal("item_selected", sources_list->get_current());
}
break;
}
}
}
// If nothing is selected, select the first entry.
if (sources_list->get_current() < 0 && sources_list->get_item_count() > 0) {
sources_list->set_current(0);
if (old_selected != int(sources_list->get_item_metadata(0))) {
sources_list->emit_signal("item_selected", sources_list->get_current());
}
}
// If there is no source left, hide all editors and show the label.
_source_selected(sources_list->get_current());
// Synchronize the lists.
TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current());
}
void TileSetEditor::_source_selected(int p_source_index) {
ERR_FAIL_COND(!tile_set.is_valid());
// Update the selected source.
sources_delete_button->set_disabled(p_source_index < 0);
if (p_source_index >= 0) {
int source_id = sources_list->get_item_metadata(p_source_index);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
if (atlas_source) {
tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id);
no_source_selected_label->hide();
tile_set_atlas_source_editor->show();
} else {
no_source_selected_label->show();
tile_set_atlas_source_editor->hide();
}
} else {
no_source_selected_label->show();
tile_set_atlas_source_editor->hide();
}
}
void TileSetEditor::_source_add_pressed() {
ERR_FAIL_COND(!tile_set.is_valid());
int source_id = tile_set->get_next_source_id();
Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
// Add a new source.
undo_redo->create_action(TTR("Add atlas source"));
undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
undo_redo->commit_action();
_update_atlas_sources_list(source_id);
}
void TileSetEditor::_source_delete_pressed() {
ERR_FAIL_COND(!tile_set.is_valid());
// Update the selected source.
int to_delete = sources_list->get_item_metadata(sources_list->get_current());
Ref<TileSetSource> source = tile_set->get_source(to_delete);
// Remove the source.
undo_redo->create_action(TTR("Remove source"));
undo_redo->add_do_method(*tile_set, "remove_source", to_delete);
undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete);
undo_redo->commit_action();
_update_atlas_sources_list();
}
void TileSetEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED:
sources_delete_button->set_icon(get_theme_icon("Remove", "EditorIcons"));
sources_add_button->set_icon(get_theme_icon("Add", "EditorIcons"));
missing_texture_texture = get_theme_icon("TileSet", "EditorIcons");
break;
case NOTIFICATION_INTERNAL_PROCESS:
if (tile_set_changed_needs_update) {
_update_atlas_sources_list();
tile_set_changed_needs_update = false;
}
break;
default:
break;
}
}
void TileSetEditor::_tile_set_changed() {
tile_set_changed_needs_update = true;
}
void TileSetEditor::_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);
#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
if (tile_set) {
Vector<String> components = p_property.split("/", true, 3);
for (int i = 0; i < tile_set->get_source_count(); i++) {
int source_id = tile_set->get_source_id(i);
Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
if (tas.is_valid()) {
for (int j = 0; j < tas->get_tiles_count(); j++) {
Vector2i tile_id = tas->get_tile_id(j);
for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
int alternative_id = tas->get_alternative_tile_id(tile_id, k);
TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
ERR_FAIL_COND(!tile_data);
if (p_property == "occlusion_layers_count") {
int new_layer_count = p_new_value;
int old_layer_count = tile_set->get_occlusion_layers_count();
if (new_layer_count < old_layer_count) {
for (int occclusion_layer_index = new_layer_count - 1; occclusion_layer_index < old_layer_count; occclusion_layer_index++) {
ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", occclusion_layer_index));
}
}
} else if (p_property == "physics_layers_count") {
int new_layer_count = p_new_value;
int old_layer_count = tile_set->get_physics_layers_count();
if (new_layer_count < old_layer_count) {
for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) {
ADD_UNDO(tile_data, vformat("physics_layer_%d/shapes_count", physics_layer_index));
for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(physics_layer_index); shape_index++) {
ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", physics_layer_index, shape_index));
ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", physics_layer_index, shape_index));
ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", physics_layer_index, shape_index));
}
}
}
} else if ((p_property == "terrains_sets_count" && tile_data->get_terrain_set() >= (int)p_new_value) ||
(components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer() && components[1] == "mode") ||
(components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) {
ADD_UNDO(tile_data, "terrain_set");
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/right_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/right_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/left_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/left_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_left_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_left_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_corner");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_right_side");
}
if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) {
ADD_UNDO(tile_data, "terrains_peering_bit/top_right_corner");
}
} else if (p_property == "navigation_layers_count") {
int new_layer_count = p_new_value;
int old_layer_count = tile_set->get_navigation_layers_count();
if (new_layer_count < old_layer_count) {
for (int navigation_layer_index = new_layer_count - 1; navigation_layer_index < old_layer_count; navigation_layer_index++) {
ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", navigation_layer_index));
}
}
} else if (p_property == "custom_data_layers_count") {
int new_layer_count = p_new_value;
int old_layer_count = tile_set->get_custom_data_layers_count();
if (new_layer_count < old_layer_count) {
for (int custom_data_layer_index = new_layer_count - 1; custom_data_layer_index < old_layer_count; custom_data_layer_index++) {
ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer_index));
}
}
} else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer() && components[1] == "type") {
int custom_data_layer = components[0].trim_prefix("custom_data_layer_").is_valid_integer();
ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer));
}
}
}
}
}
}
#undef ADD_UNDO
}
void TileSetEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetEditor::drop_data_fw);
}
TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) {
Vector<String> components = String(p_property).split("/", true);
if (p_property == "z_index") {
return tile_data_integer_editor;
} else if (p_property == "probability") {
return tile_data_float_editor;
} else if (p_property == "y_sort_origin") {
return tile_data_position_editor;
} else if (p_property == "texture_offset") {
return tile_data_texture_offset_editor;
} else if (components.size() >= 1 && components[0].begins_with("occlusion_layer_")) {
return tile_data_occlusion_shape_editor;
} else if (components.size() >= 1 && components[0].begins_with("physics_layer_")) {
return tile_data_collision_shape_editor;
} else if (p_property == "mode" || p_property == "terrain" || (components.size() >= 1 && components[0] == "terrains_peering_bit")) {
return tile_data_terrains_editor;
} else if (components.size() >= 1 && components[0].begins_with("navigation_layer_")) {
return tile_data_navigation_polygon_editor;
}
return nullptr;
}
void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
if (p_tile_set == tile_set) {
return;
}
// Remove listener.
if (tile_set.is_valid()) {
tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
}
// Change the edited object.
tile_set = p_tile_set;
// Add the listener again.
if (tile_set.is_valid()) {
tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
_update_atlas_sources_list();
}
tile_set_atlas_source_editor->hide();
no_source_selected_label->show();
}
TileSetEditor::TileSetEditor() {
singleton = this;
set_process_internal(true);
// Split container.
HSplitContainer *split_container = memnew(HSplitContainer);
split_container->set_name(TTR("Tiles"));
split_container->set_h_size_flags(SIZE_EXPAND_FILL);
split_container->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(split_container);
// Sources list.
VBoxContainer *split_container_left_side = memnew(VBoxContainer);
split_container_left_side->set_h_size_flags(SIZE_EXPAND_FILL);
split_container_left_side->set_v_size_flags(SIZE_EXPAND_FILL);
split_container_left_side->set_stretch_ratio(0.25);
split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
split_container->add_child(split_container_left_side);
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current));
sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list));
sources_list->set_drag_forwarding(this);
split_container_left_side->add_child(sources_list);
HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
sources_bottom_actions->set_alignment(HBoxContainer::ALIGN_END);
split_container_left_side->add_child(sources_bottom_actions);
sources_delete_button = memnew(Button);
sources_delete_button->set_flat(true);
sources_delete_button->set_disabled(true);
sources_delete_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_delete_pressed));
sources_bottom_actions->add_child(sources_delete_button);
sources_add_button = memnew(Button);
sources_add_button->set_flat(true);
sources_add_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_add_pressed));
sources_bottom_actions->add_child(sources_add_button);
// No source selected.
no_source_selected_label = memnew(Label);
no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source."));
no_source_selected_label->set_h_size_flags(SIZE_EXPAND_FILL);
no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL);
no_source_selected_label->set_align(Label::ALIGN_CENTER);
no_source_selected_label->set_valign(Label::VALIGN_CENTER);
split_container->add_child(no_source_selected_label);
// Atlases editor.
tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor);
tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list));
split_container->add_child(tile_set_atlas_source_editor);
tile_set_atlas_source_editor->hide();
// Registers UndoRedo inspector callback.
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
TileSetEditor::~TileSetEditor() {
if (tile_set.is_valid()) {
tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
}
// Delete tile data editors.
memdelete(tile_data_texture_offset_editor);
memdelete(tile_data_position_editor);
memdelete(tile_data_integer_editor);
memdelete(tile_data_float_editor);
memdelete(tile_data_occlusion_shape_editor);
memdelete(tile_data_collision_shape_editor);
memdelete(tile_data_terrains_editor);
memdelete(tile_data_navigation_polygon_editor);
}

View file

@ -0,0 +1,94 @@
/*************************************************************************/
/* tile_set_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILE_SET_EDITOR_H
#define TILE_SET_EDITOR_H
#include "scene/gui/box_container.h"
#include "scene/resources/tile_set.h"
#include "tile_data_editors.h"
#include "tile_set_atlas_source_editor.h"
class TileSetEditor : public VBoxContainer {
GDCLASS(TileSetEditor, VBoxContainer);
static TileSetEditor *singleton;
private:
Ref<TileSet> tile_set;
bool tile_set_changed_needs_update = false;
Label *no_source_selected_label;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor;
UndoRedo *undo_redo = EditorNode::get_undo_redo();
void _update_atlas_sources_list(int force_selected_id = -1);
// List of tile data editors.
TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor);
TileDataPositionEditor *tile_data_position_editor = memnew(TileDataPositionEditor);
TileDataIntegerEditor *tile_data_integer_editor = memnew(TileDataIntegerEditor);
TileDataFloatEditor *tile_data_float_editor = memnew(TileDataFloatEditor);
TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor);
TileDataCollisionShapeEditor *tile_data_collision_shape_editor = memnew(TileDataCollisionShapeEditor);
TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor);
TileDataNavigationPolygonEditor *tile_data_navigation_polygon_editor = memnew(TileDataNavigationPolygonEditor);
// -- Sources management --
Button *sources_delete_button;
Button *sources_add_button;
ItemList *sources_list;
Ref<Texture2D> missing_texture_texture;
void _source_selected(int p_source_index);
void _source_add_pressed();
void _source_delete_pressed();
void _tile_set_changed();
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);
static void _bind_methods();
public:
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
TileDataEditor *get_tile_data_editor(String property);
void edit(Ref<TileSet> p_tile_set);
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
TileSetEditor();
~TileSetEditor();
};
#endif // TILE_SET_EDITOR_PLUGIN_H

View file

@ -0,0 +1,276 @@
/*************************************************************************/
/* tiles_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tiles_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/tile_map.h"
#include "scene/resources/tile_set.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/separator.h"
#include "tile_set_editor.h"
TilesEditor *TilesEditor::singleton = nullptr;
void TilesEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
tileset_tilemap_switch_button->set_icon(get_theme_icon("TileSet", "EditorIcons"));
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (tile_map_changed_needs_update) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_set = tile_map->get_tileset();
}
_update_switch_button();
_update_editors();
}
} break;
}
}
void TilesEditor::_tile_map_changed() {
tile_map_changed_needs_update = true;
}
void TilesEditor::_update_switch_button() {
// Force the buttons status if needed.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map && !tile_set.is_valid()) {
tileset_tilemap_switch_button->set_pressed(false);
} else if (!tile_map && tile_set.is_valid()) {
tileset_tilemap_switch_button->set_pressed(true);
}
}
void TilesEditor::_update_editors() {
// Set editors visibility.
tilemap_toolbar->set_visible(!tileset_tilemap_switch_button->is_pressed());
tilemap_editor->set_visible(!tileset_tilemap_switch_button->is_pressed());
tileset_editor->set_visible(tileset_tilemap_switch_button->is_pressed());
// Enable/disable the switch button.
if (!tileset_tilemap_switch_button->is_pressed()) {
if (!tile_set.is_valid()) {
tileset_tilemap_switch_button->set_disabled(true);
tileset_tilemap_switch_button->set_tooltip(TTR("This TileMap has no assigned TileSet, assign a TileSet to this TileMap to edit it."));
} else {
tileset_tilemap_switch_button->set_disabled(false);
tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
}
} else {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
tileset_tilemap_switch_button->set_disabled(true);
tileset_tilemap_switch_button->set_tooltip(TTR("You are editing a TileSet resource. Select a TileMap node to paint."));
} else {
tileset_tilemap_switch_button->set_disabled(false);
tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
}
}
// If tile_map is not edited, we change the edited only if we are not editing a tile_set.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tilemap_editor->edit(tile_map);
} else {
tilemap_editor->edit(nullptr);
}
tileset_editor->edit(tile_set);
// Update the viewport
CanvasItemEditor::get_singleton()->update_viewport();
}
void TilesEditor::set_atlas_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
void TilesEditor::synchronize_atlas_sources_list(Object *p_current) {
ItemList *item_list = Object::cast_to<ItemList>(p_current);
ERR_FAIL_COND(!item_list);
if (item_list->is_visible_in_tree()) {
if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
item_list->deselect_all();
} else {
item_list->set_current(atlas_sources_lists_current);
item_list->emit_signal("item_selected", atlas_sources_lists_current);
}
}
}
void TilesEditor::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
void TilesEditor::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_COND(!tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
tile_atlas_view->set_transform(atlas_view_zoom, Vector2(atlas_view_scroll.x, atlas_view_scroll.y));
}
}
void TilesEditor::edit(Object *p_object) {
// Disconnect to changes.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_map->disconnect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
}
// Update edited objects.
tile_set = Ref<TileSet>();
if (p_object) {
if (p_object->is_class("TileMap")) {
tile_map_id = p_object->get_instance_id();
tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
tile_set = tile_map->get_tileset();
} else if (p_object->is_class("TileSet")) {
tile_set = Ref<TileSet>(p_object);
if (tile_map) {
if (tile_map->get_tileset() != tile_set) {
tile_map = nullptr;
}
}
}
// Update pressed status button.
if (p_object->is_class("TileMap")) {
tileset_tilemap_switch_button->set_pressed(false);
} else if (p_object->is_class("TileSet")) {
tileset_tilemap_switch_button->set_pressed(true);
}
}
// Update the editors.
_update_switch_button();
_update_editors();
// Add change listener.
if (tile_map) {
tile_map->connect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
}
}
void TilesEditor::_bind_methods() {
}
TilesEditor::TilesEditor(EditorNode *p_editor) {
set_process_internal(true);
// Update the singleton.
singleton = this;
// Toolbar.
HBoxContainer *toolbar = memnew(HBoxContainer);
add_child(toolbar);
// Switch button.
tileset_tilemap_switch_button = memnew(Button);
tileset_tilemap_switch_button->set_flat(true);
tileset_tilemap_switch_button->set_toggle_mode(true);
tileset_tilemap_switch_button->connect("toggled", callable_mp(this, &TilesEditor::_update_editors).unbind(1));
toolbar->add_child(tileset_tilemap_switch_button);
// Tilemap editor.
tilemap_editor = memnew(TileMapEditor);
tilemap_editor->set_h_size_flags(SIZE_EXPAND_FILL);
tilemap_editor->set_v_size_flags(SIZE_EXPAND_FILL);
tilemap_editor->hide();
add_child(tilemap_editor);
tilemap_toolbar = tilemap_editor->get_toolbar();
toolbar->add_child(tilemap_toolbar);
// Tileset editor.
tileset_editor = memnew(TileSetEditor);
tileset_editor->set_h_size_flags(SIZE_EXPAND_FILL);
tileset_editor->set_v_size_flags(SIZE_EXPAND_FILL);
tileset_editor->hide();
add_child(tileset_editor);
// Initialization.
_update_switch_button();
_update_editors();
}
TilesEditor::~TilesEditor() {
}
///////////////////////////////////////////////////////////////
void TilesEditorPlugin::_notification(int p_what) {
}
void TilesEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
tiles_editor_button->show();
editor_node->make_bottom_panel_item_visible(tiles_editor);
//get_tree()->connect_compat("idle_frame", tileset_editor, "_on_workspace_process");
} else {
editor_node->hide_bottom_panel();
tiles_editor_button->hide();
//get_tree()->disconnect_compat("idle_frame", tileset_editor, "_on_workspace_process");
}
}
void TilesEditorPlugin::edit(Object *p_object) {
tiles_editor->edit(p_object);
}
bool TilesEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("TileMap") || p_object->is_class("TileSet");
}
TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
editor_node = p_node;
tiles_editor = memnew(TilesEditor(p_node));
tiles_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
tiles_editor->hide();
tiles_editor_button = p_node->add_bottom_panel_item(TTR("Tiles"), tiles_editor);
tiles_editor_button->hide();
}
TilesEditorPlugin::~TilesEditorPlugin() {
}

View file

@ -0,0 +1,114 @@
/*************************************************************************/
/* tiles_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TILES_EDITOR_PLUGIN_H
#define TILES_EDITOR_PLUGIN_H
#include "editor/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "tile_atlas_view.h"
#include "tile_map_editor.h"
#include "tile_set_editor.h"
class TilesEditor : public VBoxContainer {
GDCLASS(TilesEditor, VBoxContainer);
static TilesEditor *singleton;
private:
bool tile_map_changed_needs_update = false;
ObjectID tile_map_id;
Ref<TileSet> tile_set;
Button *tileset_tilemap_switch_button;
Control *tilemap_toolbar;
TileMapEditor *tilemap_editor;
TileSetEditor *tileset_editor;
void _update_switch_button();
void _update_editors();
// For synchronization.
int atlas_sources_lists_current = 0;
float atlas_view_zoom = 1.0;
Vector2 atlas_view_scroll = Vector2();
void _tile_map_changed();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
_FORCE_INLINE_ static TilesEditor *get_singleton() { return singleton; }
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tilemap_editor->forward_canvas_gui_input(p_event); }
void forward_canvas_draw_over_viewport(Control *p_overlay) { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
// To synchronize the atlas sources lists.
void set_atlas_sources_lists_current(int p_current);
void synchronize_atlas_sources_list(Object *p_current);
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
void edit(Object *p_object);
TilesEditor(EditorNode *p_editor);
~TilesEditor();
};
class TilesEditorPlugin : public EditorPlugin {
GDCLASS(TilesEditorPlugin, EditorPlugin);
private:
EditorNode *editor_node;
TilesEditor *tiles_editor;
Button *tiles_editor_button;
protected:
void _notification(int p_what);
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tiles_editor->forward_canvas_gui_input(p_event); }
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tiles_editor->forward_canvas_draw_over_viewport(p_overlay); }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
TilesEditorPlugin(EditorNode *p_node);
~TilesEditorPlugin();
};
#endif // TILES_EDITOR_PLUGIN_H

File diff suppressed because it is too large Load diff

View file

@ -34,193 +34,194 @@
#include "core/templates/self_list.h"
#include "core/templates/vset.h"
#include "scene/2d/node_2d.h"
#include "scene/gui/control.h"
#include "scene/resources/tile_set.h"
class CollisionObject2D;
class TileSetAtlasSource;
union TileMapCell {
struct {
int32_t source_id : 16;
int16_t coord_x : 16;
int16_t coord_y : 16;
int32_t alternative_tile : 16;
};
uint64_t _u64t;
TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
source_id = p_source_id;
set_atlas_coords(p_atlas_coords);
alternative_tile = p_alternative_tile;
}
Vector2i get_atlas_coords() const {
return Vector2i(coord_x, coord_y);
}
void set_atlas_coords(const Vector2i &r_coords) {
coord_x = r_coords.x;
coord_y = r_coords.y;
}
bool operator<(const TileMapCell &p_other) const {
if (source_id == p_other.source_id) {
if (coord_x == p_other.coord_x) {
if (coord_y == p_other.coord_y) {
return alternative_tile < p_other.alternative_tile;
} else {
return coord_y < p_other.coord_y;
}
} else {
return coord_x < p_other.coord_x;
}
} else {
return source_id < p_other.source_id;
}
}
bool operator!=(const TileMapCell &p_other) const {
return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile);
}
};
struct TileMapQuadrant {
struct CoordsWorldComparator {
_ALWAYS_INLINE_ bool operator()(const Vector2i &p_a, const Vector2i &p_b) const {
// We sort the cells by their world coords, as it is needed by rendering.
if (p_a.y == p_b.y) {
return p_a.x > p_b.x;
} else {
return p_a.y < p_b.y;
}
}
};
// Dirty list element
SelfList<TileMapQuadrant> dirty_list_element;
// Quadrant coords.
Vector2i coords;
// TileMapCells
Set<Vector2i> cells;
// We need those two maps to sort by world position for rendering
// This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead.
Map<Vector2i, Vector2i> map_to_world;
Map<Vector2i, Vector2i, CoordsWorldComparator> world_to_map;
// Debug.
RID debug_canvas_item;
// Rendering
List<RID> canvas_items;
List<RID> occluders;
// Physics.
List<RID> bodies;
// Navigation
Map<Vector2i, Vector<RID>> navigation_regions;
void operator=(const TileMapQuadrant &q) {
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
occluders = q.occluders;
bodies = q.bodies;
navigation_regions = q.navigation_regions;
}
TileMapQuadrant(const TileMapQuadrant &q) :
dirty_list_element(this) {
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
occluders = q.occluders;
bodies = q.bodies;
navigation_regions = q.navigation_regions;
}
TileMapQuadrant() :
dirty_list_element(this) {
}
};
class TileMapPattern : public Object {
GDCLASS(TileMapPattern, Object);
Vector2i size;
Map<Vector2i, TileMapCell> pattern;
protected:
static void _bind_methods();
public:
void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0);
bool has_cell(const Vector2i &p_coords) const;
void remove_cell(const Vector2i &p_coords, bool p_update_size = true);
int get_cell_source_id(const Vector2i &p_coords) const;
Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
int get_cell_alternative_tile(const Vector2i &p_coords) const;
TypedArray<Vector2i> get_used_cells() const;
Vector2i get_size() const;
void set_size(const Vector2i &p_size);
bool is_empty() const;
void clear();
};
class TileMap : public Node2D {
GDCLASS(TileMap, Node2D);
public:
enum Mode {
MODE_SQUARE,
MODE_ISOMETRIC,
MODE_CUSTOM
};
enum HalfOffset {
HALF_OFFSET_X,
HALF_OFFSET_Y,
HALF_OFFSET_DISABLED,
HALF_OFFSET_NEGATIVE_X,
HALF_OFFSET_NEGATIVE_Y,
};
enum TileOrigin {
TILE_ORIGIN_TOP_LEFT,
TILE_ORIGIN_CENTER,
TILE_ORIGIN_BOTTOM_LEFT
};
private:
friend class TileSetPlugin;
enum DataFormat {
FORMAT_1 = 0,
FORMAT_2
FORMAT_2,
FORMAT_3
};
Ref<TileSet> tile_set;
Size2i cell_size = Size2(64, 64);
int quadrant_size = 16;
Mode mode = MODE_SQUARE;
Transform2D custom_transform = Transform2D(64, 0, 0, 64, 0, 0);
HalfOffset half_offset = HALF_OFFSET_DISABLED;
bool use_parent = false;
CollisionObject2D *collision_parent = nullptr;
bool use_kinematic = false;
bool bake_navigation = false;
int quadrant_size;
Transform2D custom_transform;
union PosKey {
struct {
int16_t x;
int16_t y;
};
uint32_t key = 0;
// Map of cells
Map<Vector2i, TileMapCell> tile_map;
//using a more precise comparison so the regions can be sorted later
bool operator<(const PosKey &p_k) const { return (y == p_k.y) ? x < p_k.x : y < p_k.y; }
Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
bool operator==(const PosKey &p_k) const { return (y == p_k.y && x == p_k.x); }
Map<Vector2i, TileMapQuadrant> quadrant_map;
PosKey to_quadrant(const int &p_quadrant_size) const {
// rounding down, instead of simply rounding towards zero (truncating)
return PosKey(
x > 0 ? x / p_quadrant_size : (x - (p_quadrant_size - 1)) / p_quadrant_size,
y > 0 ? y / p_quadrant_size : (y - (p_quadrant_size - 1)) / p_quadrant_size);
}
PosKey(int16_t p_x, int16_t p_y) {
x = p_x;
y = p_y;
}
PosKey() {
x = 0;
y = 0;
}
};
union Cell {
struct {
int32_t id : 24;
bool flip_h : 1;
bool flip_v : 1;
bool transpose : 1;
int16_t autotile_coord_x : 16;
int16_t autotile_coord_y : 16;
};
uint64_t _u64t = 0;
};
Map<PosKey, Cell> tile_map;
List<PosKey> dirty_bitmask;
struct Quadrant {
Vector2 pos;
List<RID> canvas_items;
RID body;
uint32_t shape_owner_id = 0;
SelfList<Quadrant> dirty_list;
struct NavPoly {
RID region;
Transform2D xform;
};
struct Occluder {
RID id;
Transform2D xform;
};
Map<PosKey, NavPoly> navpoly_ids;
Map<PosKey, Occluder> occluder_instances;
VSet<PosKey> cells;
void operator=(const Quadrant &q) {
pos = q.pos;
canvas_items = q.canvas_items;
body = q.body;
shape_owner_id = q.shape_owner_id;
cells = q.cells;
navpoly_ids = q.navpoly_ids;
occluder_instances = q.occluder_instances;
}
Quadrant(const Quadrant &q) :
dirty_list(this) {
pos = q.pos;
canvas_items = q.canvas_items;
body = q.body;
shape_owner_id = q.shape_owner_id;
cells = q.cells;
occluder_instances = q.occluder_instances;
navpoly_ids = q.navpoly_ids;
}
Quadrant() :
dirty_list(this) {}
};
Map<PosKey, Quadrant> quadrant_map;
SelfList<Quadrant>::List dirty_quadrant_list;
SelfList<TileMapQuadrant>::List dirty_quadrant_list;
bool pending_update = false;
Rect2 rect_cache;
bool rect_cache_dirty = true;
Rect2 used_size_cache;
bool used_size_cache_dirty = true;
bool quadrant_order_dirty = false;
bool use_y_sort = false;
bool compatibility_mode = false;
bool centered_textures = false;
bool clip_uv = false;
float fp_adjust = 0.00001;
float friction = 1.0;
float bounce = 0.0;
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present
bool used_size_cache_dirty;
mutable DataFormat format;
TileOrigin tile_origin = TILE_ORIGIN_TOP_LEFT;
void _fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
int occluder_light_mask = 1;
void _fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
void _add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata);
Map<PosKey, Quadrant>::Element *_create_quadrant(const PosKey &p_qk);
void _erase_quadrant(Map<PosKey, Quadrant>::Element *Q);
void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true);
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 _update_quadrant_space(const RID &p_space);
void _update_quadrant_transform();
void _recompute_rect_cache();
void _update_all_items_material_state();
_FORCE_INLINE_ void _update_item_material_state(const RID &p_canvas_item);
_FORCE_INLINE_ int _get_quadrant_size() const;
void _set_tile_data(const Vector<int> &p_data);
Vector<int> _get_tile_data() const;
void _set_old_cell_size(int p_size) { set_cell_size(Size2(p_size, p_size)); }
int _get_old_cell_size() const { return cell_size.x; }
_FORCE_INLINE_ Vector2 _map_to_world(int p_x, int p_y, bool p_ignore_ofs = false) const;
void _tile_set_changed();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -230,9 +231,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
public:
static Vector2i transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
enum {
INVALID_CELL = -1
};
@ -244,117 +245,49 @@ public:
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
void set_cell_size(Size2 p_size);
Size2 get_cell_size() const;
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
void set_cell(int p_x, int p_y, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false, Vector2 p_autotile_coord = Vector2());
int get_cell(int p_x, int p_y) const;
bool is_cell_x_flipped(int p_x, int p_y) const;
bool is_cell_y_flipped(int p_x, int p_y) const;
bool is_cell_transposed(int p_x, int p_y) const;
void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord);
Vector2 get_cell_autotile_coord(int p_x, int p_y) const;
void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
int get_cell_source_id(const Vector2i &p_coords) const;
Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
int get_cell_alternative_tile(const Vector2i &p_coords) const;
void _set_celld(const Vector2 &p_pos, const Dictionary &p_data);
void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false);
int get_cellv(const Vector2 &p_pos) const;
TileMapPattern *get_pattern(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 make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2());
void update_cell_bitmask(int p_x, int p_y);
void update_dirty_bitmask();
// Not exposed to users
TileMapCell get_cell(const Vector2i &p_coords) const;
Map<Vector2i, TileMapQuadrant> &get_quadrant_map();
int get_effective_quadrant_size() const;
void update_dirty_quadrants();
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
Vector2 map_to_world(const Vector2 &p_pos) const;
Vector2i world_to_map(const Vector2 &p_pos) const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
void set_collision_layer_bit(int p_bit, bool p_value);
bool get_collision_layer_bit(int p_bit) const;
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
void set_collision_use_kinematic(bool p_use_kinematic);
bool get_collision_use_kinematic() const;
void set_collision_use_parent(bool p_use_parent);
bool get_collision_use_parent() const;
void set_collision_friction(float p_friction);
float get_collision_friction() const;
void set_collision_bounce(float p_bounce);
float get_collision_bounce() const;
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_half_offset(HalfOffset p_half_offset);
HalfOffset get_half_offset() const;
void set_tile_origin(TileOrigin p_tile_origin);
TileOrigin get_tile_origin() const;
void set_custom_transform(const Transform2D &p_xform);
Transform2D get_custom_transform() const;
Transform2D get_cell_transform() const;
Vector2 get_cell_draw_offset() const;
Vector2 map_to_world(const Vector2 &p_pos, bool p_ignore_ofs = false) const;
Vector2 world_to_map(const Vector2 &p_pos) const;
void set_y_sort_enabled(bool p_enable);
bool is_y_sort_enabled() const;
void set_compatibility_mode(bool p_enable);
bool is_compatibility_mode_enabled() const;
void set_centered_textures(bool p_enable);
bool is_centered_textures_enabled() const;
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_by_index(int p_index) const;
Rect2 get_used_rect(); // Not const because of cache
void set_occluder_light_mask(int p_mask);
int get_occluder_light_mask() const;
// Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems
virtual void set_light_mask(int p_light_mask) override;
virtual void set_material(const Ref<Material> &p_material) override;
virtual void set_use_parent_material(bool p_use_parent_material) override;
void set_clip_uv(bool p_enable);
bool get_clip_uv() const;
TypedArray<String> get_configuration_warnings() const override;
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
void fix_invalid_tiles();
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());
TileMap();
~TileMap();
};
VARIANT_ENUM_CAST(TileMap::Mode);
VARIANT_ENUM_CAST(TileMap::HalfOffset);
VARIANT_ENUM_CAST(TileMap::TileOrigin);
#endif // TILE_MAP_H

View file

@ -406,6 +406,9 @@ void ItemList::remove_item(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
items.remove(p_idx);
if (current == p_idx) {
current = -1;
}
update();
shape_changed = true;
defer_select_single = -1;

View file

@ -655,6 +655,9 @@ void register_scene_types() {
ClassDB::register_class<GrooveJoint2D>();
ClassDB::register_class<DampedSpringJoint2D>();
ClassDB::register_class<TileSet>();
ClassDB::register_virtual_class<TileSetSource>();
ClassDB::register_class<TileSetAtlasSource>();
ClassDB::register_class<TileData>();
ClassDB::register_class<TileMap>();
ClassDB::register_class<ParallaxBackground>();
ClassDB::register_class<ParallaxLayer>();

File diff suppressed because it is too large Load diff

View file

@ -32,226 +32,614 @@
#define TILE_SET_H
#include "core/io/resource.h"
#include "core/variant/array.h"
#include "core/object/object.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/navigation_region_2d.h"
#include "scene/main/canvas_item.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/physics_material.h"
#include "scene/resources/shape_2d.h"
#ifndef DISABLE_DEPRECATED
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/navigation_region_2d.h"
#include "scene/resources/shader.h"
#include "scene/resources/shape_2d.h"
#include "scene/resources/texture.h"
#endif
class TileMap;
struct TileMapQuadrant;
class TileSetSource;
class TileSetAtlasSource;
class TileData;
// Forward-declare the plugins.
class TileSetPlugin;
class TileSetAtlasPluginRendering;
class TileSetAtlasPluginPhysics;
class TileSetAtlasPluginNavigation;
class TileSetAtlasPluginTerrain;
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
public:
struct ShapeData {
Ref<Shape2D> shape;
Transform2D shape_transform;
Vector2 autotile_coord;
bool one_way_collision = false;
float one_way_collision_margin = 1.0;
ShapeData() {}
};
enum BitmaskMode {
BITMASK_2X2,
BITMASK_3X3_MINIMAL,
BITMASK_3X3
};
enum AutotileBindings {
BIND_TOPLEFT = 1,
BIND_TOP = 2,
BIND_TOPRIGHT = 4,
BIND_LEFT = 8,
BIND_CENTER = 16,
BIND_RIGHT = 32,
BIND_BOTTOMLEFT = 64,
BIND_BOTTOM = 128,
BIND_BOTTOMRIGHT = 256,
BIND_IGNORE_TOPLEFT = 1 << 16,
BIND_IGNORE_TOP = 1 << 17,
BIND_IGNORE_TOPRIGHT = 1 << 18,
BIND_IGNORE_LEFT = 1 << 19,
BIND_IGNORE_CENTER = 1 << 20,
BIND_IGNORE_RIGHT = 1 << 21,
BIND_IGNORE_BOTTOMLEFT = 1 << 22,
BIND_IGNORE_BOTTOM = 1 << 23,
BIND_IGNORE_BOTTOMRIGHT = 1 << 24
};
enum TileMode {
SINGLE_TILE,
AUTO_TILE,
ATLAS_TILE
};
struct AutotileData {
BitmaskMode bitmask_mode = BITMASK_2X2;
// Default size to prevent invalid value
Size2 size = Size2(64, 64);
Vector2 icon_coord = Vector2(0, 0);
int spacing = 0;
Map<Vector2, uint32_t> flags;
Map<Vector2, Ref<OccluderPolygon2D>> occluder_map;
Map<Vector2, Ref<NavigationPolygon>> navpoly_map;
Map<Vector2, int> priority_map;
Map<Vector2, int> z_index_map;
explicit AutotileData() {}
};
#ifndef DISABLE_DEPRECATED
private:
struct TileData {
struct CompatibilityShapeData {
Vector2i autotile_coords;
bool one_way;
float one_way_margin;
Ref<Shape2D> shape;
Transform2D transform;
};
struct CompatibilityTileData {
String name;
Ref<Texture2D> texture;
Vector2 offset;
Rect2i region;
Vector<ShapeData> shapes_data;
Vector2 occluder_offset;
Ref<OccluderPolygon2D> occluder;
Vector2 navigation_polygon_offset;
Ref<NavigationPolygon> navigation_polygon;
Vector2 tex_offset;
Ref<ShaderMaterial> material;
TileMode tile_mode = SINGLE_TILE;
// Default modulate for back-compat
Color modulate = Color(1, 1, 1);
AutotileData autotile_data;
int z_index = 0;
Rect2 region;
int tile_mode;
Color modulate;
explicit TileData() {}
// Atlas or autotiles data
int autotile_bitmask_mode;
Vector2 autotile_icon_coordinate;
Size2i autotile_tile_size = Size2i(16, 16);
int autotile_spacing;
Map<Vector2i, int> autotile_bitmask_flags;
Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
Map<Vector2i, int> autotile_priority_map;
Map<Vector2i, int> autotile_z_index_map;
Vector<CompatibilityShapeData> shapes;
Ref<OccluderPolygon2D> occluder;
Vector2 occluder_offset;
Ref<NavigationPolygon> navigation;
Vector2 navigation_offset;
int z_index;
};
Map<int, TileData> tile_map;
Map<int, CompatibilityTileData *> compatibility_data = Map<int, CompatibilityTileData *>();
Map<int, int> compatibility_source_mapping = Map<int, int>();
private:
void compatibility_conversion();
public:
int compatibility_get_source_for_tile_id(int p_old_source) {
return compatibility_source_mapping[p_old_source];
};
#endif // DISABLE_DEPRECATED
public:
enum CellNeighbor {
CELL_NEIGHBOR_RIGHT_SIDE = 0,
CELL_NEIGHBOR_RIGHT_CORNER,
CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
CELL_NEIGHBOR_BOTTOM_SIDE,
CELL_NEIGHBOR_BOTTOM_CORNER,
CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
CELL_NEIGHBOR_LEFT_SIDE,
CELL_NEIGHBOR_LEFT_CORNER,
CELL_NEIGHBOR_TOP_LEFT_SIDE,
CELL_NEIGHBOR_TOP_LEFT_CORNER,
CELL_NEIGHBOR_TOP_SIDE,
CELL_NEIGHBOR_TOP_CORNER,
CELL_NEIGHBOR_TOP_RIGHT_SIDE,
CELL_NEIGHBOR_TOP_RIGHT_CORNER,
CELL_NEIGHBOR_MAX,
};
enum TerrainMode {
TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0,
TERRAIN_MODE_MATCH_CORNERS,
TERRAIN_MODE_MATCH_SIDES,
};
enum TileShape {
TILE_SHAPE_SQUARE,
TILE_SHAPE_ISOMETRIC,
TILE_SHAPE_HALF_OFFSET_SQUARE,
TILE_SHAPE_HEXAGON,
};
enum TileLayout {
TILE_LAYOUT_STACKED,
TILE_LAYOUT_STACKED_OFFSET,
TILE_LAYOUT_STAIRS_RIGHT,
TILE_LAYOUT_STAIRS_DOWN,
TILE_LAYOUT_DIAMOND_RIGHT,
TILE_LAYOUT_DIAMOND_DOWN,
};
enum TileOffsetAxis {
TILE_OFFSET_AXIS_HORIZONTAL,
TILE_OFFSET_AXIS_VERTICAL,
};
public:
struct PackedSceneSource {
Ref<PackedScene> scene;
Vector2 offset;
};
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _tile_set_shapes(int p_id, const Array &p_shapes);
Array _tile_get_shapes(int p_id) const;
Array _get_tiles_ids() const;
void _decompose_convex_shape(Ref<Shape2D> p_shape);
private:
// --- TileSet data ---
// Basic shape and layout.
TileShape tile_shape = TILE_SHAPE_SQUARE;
TileLayout tile_layout = TILE_LAYOUT_STACKED;
TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL;
Size2i tile_size = Size2i(16, 16); //Size2(64, 64);
Vector2 tile_skew = Vector2(0, 0);
// Rendering.
bool y_sorting = false;
bool uv_clipping = false;
struct OcclusionLayer {
uint32_t light_mask = 1;
bool sdf_collision = false;
};
Vector<OcclusionLayer> occlusion_layers;
// Physics
struct PhysicsLayer {
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
Ref<PhysicsMaterial> physics_material;
};
Vector<PhysicsLayer> physics_layers;
// Terrains
struct Terrain {
String name;
Color color;
};
struct TerrainSet {
TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES;
Vector<Terrain> terrains;
};
Vector<TerrainSet> terrain_sets;
// Navigation
struct Navigationlayer {
uint32_t layers = 1;
};
Vector<Navigationlayer> navigation_layers;
// CustomData
struct CustomDataLayer {
String name;
Variant::Type type = Variant::NIL;
};
Vector<CustomDataLayer> custom_data_layers;
Map<String, int> custom_data_layers_by_name;
// Per Atlas source data.
Map<int, Ref<TileSetSource>> sources;
Vector<int> source_ids;
int next_source_id = 0;
// ---------------------
// Plugins themselves.
Vector<TileSetPlugin *> tile_set_plugins_vector;
void _compute_next_source_id();
void _source_changed();
protected:
static void _bind_methods();
public:
// --- Plugins ---
Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const;
// --- Accessors for TileSet data ---
// -- Shape and layout --
void set_tile_shape(TileShape p_shape);
TileShape get_tile_shape() const;
void set_tile_layout(TileLayout p_layout);
TileLayout get_tile_layout() const;
void set_tile_offset_axis(TileOffsetAxis p_alignment);
TileOffsetAxis get_tile_offset_axis() const;
void set_tile_size(Size2i p_size);
Size2i get_tile_size() const;
void set_tile_skew(Vector2 p_skew);
Vector2 get_tile_skew() const;
// -- Sources management --
int get_next_source_id() const;
int get_source_count() const;
int get_source_id(int p_index) const;
int add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_source_id_override = -1);
void set_source_id(int p_source_id, int p_new_id);
void remove_source(int p_source_id);
bool has_source(int p_source_id) const;
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;
void set_occlusion_layers_count(int p_occlusion_layers_count);
int get_occlusion_layers_count() const;
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
int get_occlusion_layer_light_mask(int p_layer_index) const;
void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
// Physics
void set_physics_layers_count(int p_physics_layers_count);
int get_physics_layers_count() const;
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
// Terrains
void set_terrain_sets_count(int p_terrains_sets_count);
int get_terrain_sets_count() const;
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
void set_terrains_count(int p_terrain_set, int p_terrains_count);
int get_terrains_count(int p_terrain_set) const;
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
// Navigation
void set_navigation_layers_count(int p_navigation_layers_count);
int get_navigation_layers_count() const;
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
// Custom data
void set_custom_data_layers_count(int p_custom_data_layers_count);
int get_custom_data_layers_count() const;
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_name(int p_layer_id, String p_value);
String get_custom_data_name(int p_layer_id) const;
void set_custom_data_type(int p_layer_id, Variant::Type p_value);
Variant::Type get_custom_data_type(int p_layer_id) const;
// Helpers
void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
virtual void reset_state() override;
public:
void create_tile(int p_id);
void autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode);
BitmaskMode autotile_get_bitmask_mode(int p_id) const;
void tile_set_name(int p_id, const String &p_name);
String tile_get_name(int p_id) const;
void tile_set_texture(int p_id, const Ref<Texture2D> &p_texture);
Ref<Texture2D> tile_get_texture(int p_id) const;
void tile_set_texture_offset(int p_id, const Vector2 &p_offset);
Vector2 tile_get_texture_offset(int p_id) const;
void tile_set_region(int p_id, const Rect2 &p_region);
Rect2 tile_get_region(int p_id) const;
void tile_set_tile_mode(int p_id, TileMode p_tile_mode);
TileMode tile_get_tile_mode(int p_id) const;
void autotile_set_icon_coordinate(int p_id, Vector2 coord);
Vector2 autotile_get_icon_coordinate(int p_id) const;
void autotile_set_spacing(int p_id, int p_spacing);
int autotile_get_spacing(int p_id) const;
void autotile_set_size(int p_id, Size2 p_size);
Size2 autotile_get_size(int p_id) const;
void autotile_clear_bitmask_map(int p_id);
void autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority);
int autotile_get_subtile_priority(int p_id, const Vector2 &p_coord);
const Map<Vector2, int> &autotile_get_priority_map(int p_id) const;
void autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index);
int autotile_get_z_index(int p_id, const Vector2 &p_coord);
const Map<Vector2, int> &autotile_get_z_index_map(int p_id) const;
void autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag);
uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord);
const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id);
Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape);
Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const;
void tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset);
Transform2D tile_get_shape_transform(int p_id, int p_shape_id) const;
void tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset);
Vector2 tile_get_shape_offset(int p_id, int p_shape_id) const;
void tile_set_shape_one_way(int p_id, int p_shape_id, bool p_one_way);
bool tile_get_shape_one_way(int p_id, int p_shape_id) const;
void tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin);
float tile_get_shape_one_way_margin(int p_id, int p_shape_id) const;
void tile_clear_shapes(int p_id);
void tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way = false, const Vector2 &p_autotile_coord = Vector2());
int tile_get_shape_count(int p_id) const;
void tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes);
Vector<ShapeData> tile_get_shapes(int p_id) const;
void tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material);
Ref<ShaderMaterial> tile_get_material(int p_id) const;
void tile_set_modulate(int p_id, const Color &p_modulate);
Color tile_get_modulate(int p_id) const;
void tile_set_occluder_offset(int p_id, const Vector2 &p_offset);
Vector2 tile_get_occluder_offset(int p_id) const;
void tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder);
Ref<OccluderPolygon2D> tile_get_light_occluder(int p_id) const;
void autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord);
Ref<OccluderPolygon2D> autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const;
const Map<Vector2, Ref<OccluderPolygon2D>> &autotile_get_light_oclusion_map(int p_id) const;
void tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset);
Vector2 tile_get_navigation_polygon_offset(int p_id) const;
void tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon);
Ref<NavigationPolygon> tile_get_navigation_polygon(int p_id) const;
void autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord);
Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const;
const Map<Vector2, Ref<NavigationPolygon>> &autotile_get_navigation_map(int p_id) const;
void tile_set_z_index(int p_id, int p_z_index);
int tile_get_z_index(int p_id) const;
void remove_tile(int p_id);
bool has_tile(int p_id) const;
bool is_tile_bound(int p_drawn_id, int p_neighbor_id);
int find_tile_by_name(const String &p_name) const;
void get_tile_list(List<int> *p_tiles) const;
void clear();
int get_last_unused_tile_id() const;
TileSet();
~TileSet();
};
VARIANT_ENUM_CAST(TileSet::AutotileBindings);
VARIANT_ENUM_CAST(TileSet::BitmaskMode);
VARIANT_ENUM_CAST(TileSet::TileMode);
class TileSetSource : public Resource {
GDCLASS(TileSetSource, Resource);
protected:
const TileSet *tile_set = nullptr;
public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
virtual void notify_tile_data_properties_should_change(){};
virtual void reset_state() override{};
// Tiles.
virtual int get_tiles_count() const = 0;
virtual Vector2i get_tile_id(int tile_index) const = 0;
virtual bool has_tile(Vector2i p_atlas_coords) const = 0;
// Alternative tiles.
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0;
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0;
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0;
};
class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
public:
static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
static const int INVALID_TILE_ALTERNATIVE; // -1;
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
Vector2i texture_offset;
Map<int, TileData *> alternatives;
Vector<int> alternatives_ids;
int next_alternative_id = 1;
};
private:
Ref<Texture2D> texture;
Vector2i margins;
Vector2i separation;
Size2i texture_region_size = Size2i(16, 16);
Map<Vector2i, TileAlternativesData> tiles;
Vector<Vector2i> tiles_ids;
Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
virtual void notify_tile_data_properties_should_change() override;
virtual void reset_state() override;
// Base properties.
void set_texture(Ref<Texture2D> p_texture);
Ref<Texture2D> get_texture() const;
void set_margins(Vector2i p_margins);
Vector2i get_margins() const;
void set_separation(Vector2i p_separation);
Vector2i get_separation() const;
void set_texture_region_size(Vector2i p_tile_size);
Vector2i get_texture_region_size() const;
// Base tiles.
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does.
void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
virtual bool has_tile(Vector2i p_atlas_coords) const override;
bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const;
void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
virtual int get_tiles_count() const override;
virtual Vector2i get_tile_id(int p_index) const override;
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
// Alternative tiles.
int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id);
virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const;
virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
// Get data associated to a tile.
Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
// Helpers.
Vector2i get_atlas_grid_size() const;
bool has_tiles_outside_texture();
void clear_tiles_outside_texture();
Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
~TileSetAtlasSource();
};
class TileData : public Object {
GDCLASS(TileData, Object);
private:
const TileSet *tile_set = nullptr;
bool allow_transform = true;
// Rendering
bool flip_h = false;
bool flip_v = false;
bool transpose = false;
Vector2i tex_offset = Vector2i();
Ref<ShaderMaterial> material = Ref<ShaderMaterial>();
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
Vector2i y_sort_origin = Vector2i();
Vector<Ref<OccluderPolygon2D>> occluders;
// Physics
struct PhysicsLayerTileData {
struct ShapeTileData {
Ref<Shape2D> shape = Ref<Shape2D>();
bool one_way = false;
float one_way_margin = 1.0;
};
Vector<ShapeTileData> shapes;
};
Vector<PhysicsLayerTileData> physics;
// TODO add support for areas.
// Terrain
int terrain_set = -1;
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
// Navigation
Vector<Ref<NavigationPolygon>> navigation;
// Misc
double probability = 1.0;
// Custom data
Vector<Variant> custom_data;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
void notify_tile_data_properties_should_change();
void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
// Rendering
void set_flip_h(bool p_flip_h);
bool get_flip_h() const;
void set_flip_v(bool p_flip_v);
bool get_flip_v() const;
void set_transpose(bool p_transpose);
bool get_transpose() const;
void set_texture_offset(Vector2i p_texture_offset);
Vector2i get_texture_offset() const;
void tile_set_material(Ref<ShaderMaterial> p_material);
Ref<ShaderMaterial> tile_get_material() const;
void set_modulate(Color p_modulate);
Color get_modulate() const;
void set_z_index(int p_z_index);
int get_z_index() const;
void set_y_sort_origin(Vector2i p_y_sort_origin);
Vector2i get_y_sort_origin() const;
void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
// Physics
int get_collision_shapes_count(int p_layer_id) const;
void set_collision_shapes_count(int p_layer_id, int p_shapes_count);
void add_collision_shape(int p_layer_id);
void remove_collision_shape(int p_layer_id, int p_shape_index);
void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape);
Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const;
void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way);
bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const;
void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin);
float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const;
// Terrain
void set_terrain_set(int p_terrain_id);
int get_terrain_set() const;
void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
// Navigation
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
// Misc
void set_probability(float p_probability);
float get_probability() const;
// Custom data.
void set_custom_data(String p_layer_name, Variant p_value);
Variant get_custom_data(String p_layer_name) const;
void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
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 TileSetAtlasPluginRendering : public TileSetPlugin {
GDCLASS(TileSetAtlasPluginRendering, 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;
// 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 TileSetAtlasPluginTerrain : public TileSetPlugin {
GDCLASS(TileSetAtlasPluginTerrain, TileSetPlugin);
private:
static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
public:
//virtual void tilemap_notification(const TileMap * p_tile_map, int p_what);
static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data);
};
class TileSetAtlasPluginPhysics : public TileSetPlugin {
GDCLASS(TileSetAtlasPluginPhysics, 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 TileSetAtlasPluginNavigation : public TileSetPlugin {
GDCLASS(TileSetAtlasPluginNavigation, 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;
};
VARIANT_ENUM_CAST(TileSet::CellNeighbor);
VARIANT_ENUM_CAST(TileSet::TerrainMode);
VARIANT_ENUM_CAST(TileSet::TileShape);
VARIANT_ENUM_CAST(TileSet::TileLayout);
VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
#endif // TILE_SET_H