From 14e2a991295aaccf68e19088db56e79541e1a8a8 Mon Sep 17 00:00:00 2001 From: Ranoller Date: Mon, 1 Jul 2019 22:43:52 +0200 Subject: [PATCH] Tilemap fix displaced textures and shapes and added center texture and compatibility mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fix #22989 #15249 #28206. Main problem is that tilemap displace textures in different tile origins in a strange way and doesn´t respect coincidence between texture and shapes in not uniform tiles. This issue is present in godot 3.0 and godot 3.1. To maintain compatibility are added a compatibility mode and a center texture option. Other related issues and pull request: #28896 #29487 #29519 #29961. Idications of #30204 are added --- doc/classes/TileMap.xml | 9 ++ editor/plugins/tile_map_editor_plugin.cpp | 65 ++++++++-- scene/2d/tile_map.cpp | 147 +++++++++++++++++++--- scene/2d/tile_map.h | 8 ++ 4 files changed, 207 insertions(+), 22 deletions(-) diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 55666b94b59..a0111d32626 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -274,6 +274,15 @@ If [code]true[/code], the TileMap's children will be drawn in order of their Y coordinate. + + 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. + + + 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. + Bounce value for static body collisions (see [code]collision_use_kinematic[/code]). diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 6e47d828474..3735cceb152 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -782,8 +782,9 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p r.position += (r.size + Vector2(spacing, spacing)) * offset; } Size2 sc = p_xform.get_scale(); - /* For a future CheckBox to Center Texture: - Size2 cell_size = node->get_cell_size(); */ + Size2 cell_size = node->get_cell_size(); + bool centered_texture = node->is_centered_textures_enabled(); + bool compatibility_mode_enabled = node->is_compatibility_mode_enabled(); Rect2 rect = Rect2(); rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset(); @@ -793,13 +794,24 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p rect.size = r.size; } + if (compatibility_mode_enabled && !centered_texture) { + if (rect.size.y > rect.size.x) { + if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose)) + tile_ofs.y += rect.size.y - rect.size.x; + } else if (rect.size.y < rect.size.x) { + if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose)) + tile_ofs.x += rect.size.x - rect.size.y; + } + } + if (p_transpose) { SWAP(tile_ofs.x, tile_ofs.y); - /* For a future CheckBox to Center Texture: - rect.position.x += cell_size.x / 2 - rect.size.y / 2; - rect.position.y += cell_size.y / 2 - rect.size.x / 2; - } else { - rect.position += cell_size / 2 - rect.size / 2; */ + if (centered_texture) { + rect.position.x += cell_size.x / 2 - rect.size.y / 2; + rect.position.y += cell_size.y / 2 - rect.size.x / 2; + } + } else if (centered_texture) { + rect.position += cell_size / 2 - rect.size / 2; } if (p_flip_h) { @@ -812,7 +824,44 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p tile_ofs.y *= -1.0; } - rect.position += tile_ofs; + if (compatibility_mode_enabled && !centered_texture) { + if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) { + + rect.position += tile_ofs; + } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) { + + rect.position += tile_ofs; + + if (p_transpose) { + if (p_flip_h) + rect.position.x -= cell_size.x; + else + rect.position.x += cell_size.x; + } else { + if (p_flip_v) + rect.position.y -= cell_size.y; + else + rect.position.y += cell_size.y; + } + + } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) { + + rect.position += tile_ofs; + + if (p_flip_h) + rect.position.x -= cell_size.x / 2; + else + rect.position.x += cell_size.x / 2; + + if (p_flip_v) + rect.position.y -= cell_size.y / 2; + else + rect.position.y += cell_size.y / 2; + } + } else { + rect.position += tile_ofs; + } + rect.position = p_xform.xform(rect.position); rect.size *= sc; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index fc53c9e4ace..22bc0e44e69 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -234,6 +234,25 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Size2 s = p_sc; Vector2 offset = p_offset; + if (compatibility_mode && !centered_textures) { + + if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { + offset.y += cell_size.y; + } else if (tile_origin == TILE_ORIGIN_CENTER) { + offset += cell_size / 2; + } + + if (s.y > s.x) { + if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) { + offset.y += s.y - s.x; + } + } else if (s.y < s.x) { + if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose)) { + offset.x += s.x - s.y; + } + } + } + if (p_cell.transpose) { SWAP(xform.elements[0].x, xform.elements[0].y); SWAP(xform.elements[1].x, xform.elements[1].y); @@ -244,16 +263,36 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const if (p_cell.flip_h) { xform.elements[0].x = -xform.elements[0].x; xform.elements[1].x = -xform.elements[1].x; - offset.x = s.x - offset.x; + if (compatibility_mode && !centered_textures) { + if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { + offset.x = s.x - offset.x; + } else if (tile_origin == TILE_ORIGIN_CENTER) { + offset.x = s.x - offset.x / 2; + } + } else { + offset.x = s.x - offset.x; + } } if (p_cell.flip_v) { xform.elements[0].y = -xform.elements[0].y; xform.elements[1].y = -xform.elements[1].y; - offset.y = s.y - offset.y; + if (compatibility_mode && !centered_textures) { + if (tile_origin == TILE_ORIGIN_TOP_LEFT) { + offset.y = s.y - offset.y; + } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { + offset.y += s.y; + } else if (tile_origin == TILE_ORIGIN_CENTER) { + offset.y += s.y; + } + } else { + offset.y = s.y - offset.y; + } + } + + if (centered_textures) { + offset += cell_size / 2 - s / 2; } - /* For a future CheckBox to Center Texture: - offset += cell_size / 2 - s / 2; */ xform.elements[2] += offset; } @@ -434,13 +473,24 @@ void TileMap::update_dirty_quadrants() { rect.size.x += fp_adjust; rect.size.y += fp_adjust; + if (compatibility_mode && !centered_textures) { + if (rect.size.y > rect.size.x) { + if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose)) + tile_ofs.y += rect.size.y - rect.size.x; + } else if (rect.size.y < rect.size.x) { + if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose)) + tile_ofs.x += rect.size.x - rect.size.y; + } + } + if (c.transpose) { SWAP(tile_ofs.x, tile_ofs.y); - /* For a future CheckBox to Center Texture: - rect.position.x += cell_size.x / 2 - rect.size.y / 2; - rect.position.y += cell_size.y / 2 - rect.size.x / 2; - } else { - rect.position += cell_size / 2 - rect.size / 2; */ + if (centered_textures) { + rect.position.x += cell_size.x / 2 - rect.size.y / 2; + rect.position.y += cell_size.y / 2 - rect.size.x / 2; + } + } else if (centered_textures) { + rect.position += cell_size / 2 - rect.size / 2; } if (c.flip_h) { @@ -453,7 +503,43 @@ void TileMap::update_dirty_quadrants() { tile_ofs.y = -tile_ofs.y; } - rect.position += tile_ofs; + if (compatibility_mode && !centered_textures) { + if (tile_origin == TILE_ORIGIN_TOP_LEFT) { + rect.position += tile_ofs; + + } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) { + + rect.position += tile_ofs; + + if (c.transpose) { + if (c.flip_h) + rect.position.x -= cell_size.x; + else + rect.position.x += cell_size.x; + } else { + if (c.flip_v) + rect.position.y -= cell_size.y; + else + rect.position.y += cell_size.y; + } + + } else if (tile_origin == TILE_ORIGIN_CENTER) { + + rect.position += tile_ofs; + + if (c.flip_h) + rect.position.x -= cell_size.x / 2; + else + rect.position.x += cell_size.x / 2; + + if (c.flip_v) + rect.position.y -= cell_size.y / 2; + else + rect.position.y += cell_size.y / 2; + } + } else { + rect.position += tile_ofs; + } Ref normal_map = tile_set->tile_get_normal_map(c.id); Color modulate = tile_set->tile_get_modulate(c.id); @@ -1156,10 +1242,7 @@ void TileMap::_set_tile_data(const PoolVector &p_data) { coord_x = decode_uint16(&local[8]); coord_y = decode_uint16(&local[10]); } - /* - if (x<-20 || y <-20 || x>4000 || y>4000) - continue; - */ + set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y)); } @@ -1570,6 +1653,32 @@ bool TileMap::is_y_sort_mode_enabled() const { return y_sort_mode; } +void TileMap::set_compatibility_mode(bool p_enable) { + + _clear_quadrants(); + compatibility_mode = p_enable; + _recreate_quadrants(); + emit_signal("settings_changed"); +} + +bool TileMap::is_compatibility_mode_enabled() const { + + return compatibility_mode; +} + +void TileMap::set_centered_textures(bool p_enable) { + + _clear_quadrants(); + centered_textures = p_enable; + _recreate_quadrants(); + emit_signal("settings_changed"); +} + +bool TileMap::is_centered_textures_enabled() const { + + return centered_textures; +} + Array TileMap::get_used_cells() const { Array a; @@ -1707,6 +1816,12 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_y_sort_mode", "enable"), &TileMap::set_y_sort_mode); ClassDB::bind_method(D_METHOD("is_y_sort_mode_enabled"), &TileMap::is_y_sort_mode_enabled); + ClassDB::bind_method(D_METHOD("set_compatibility_mode", "enable"), &TileMap::set_compatibility_mode); + ClassDB::bind_method(D_METHOD("is_compatibility_mode_enabled"), &TileMap::is_compatibility_mode_enabled); + + ClassDB::bind_method(D_METHOD("set_centered_textures", "enable"), &TileMap::set_centered_textures); + ClassDB::bind_method(D_METHOD("is_centered_textures_enabled"), &TileMap::is_centered_textures_enabled); + ClassDB::bind_method(D_METHOD("set_collision_use_kinematic", "use_kinematic"), &TileMap::set_collision_use_kinematic); ClassDB::bind_method(D_METHOD("get_collision_use_kinematic"), &TileMap::get_collision_use_kinematic); @@ -1775,6 +1890,8 @@ void TileMap::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_mode", "is_y_sort_mode_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compatibility_mode"), "set_compatibility_mode", "is_compatibility_mode_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered_textures"), "set_centered_textures", "is_centered_textures_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv"); ADD_GROUP("Collision", "collision_"); @@ -1832,6 +1949,8 @@ TileMap::TileMap() { use_kinematic = false; navigation = NULL; y_sort_mode = false; + compatibility_mode = false; + centered_textures = false; occluder_light_mask = 1; clip_uv = false; format = FORMAT_1; //Always initialize with the lowest format diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 1caaefa2135..6c9648ff326 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -181,6 +181,8 @@ private: bool used_size_cache_dirty; bool quadrant_order_dirty; bool y_sort_mode; + bool compatibility_mode; + bool centered_textures; bool clip_uv; float fp_adjust; float friction; @@ -311,6 +313,12 @@ public: void set_y_sort_mode(bool p_enable); bool is_y_sort_mode_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; + Array get_used_cells() const; Array get_used_cells_by_id(int p_id) const; Rect2 get_used_rect(); // Not const because of cache