Prevent tiles outside atlas texture

This commit is contained in:
Gilles Roudière 2021-10-11 12:04:40 +02:00
parent 3be18d3d37
commit b9151860f3
7 changed files with 151 additions and 124 deletions

View file

@ -15,12 +15,6 @@
<tutorials>
</tutorials>
<methods>
<method name="clear_tiles_outside_texture">
<return type="void" />
<description>
Clears all tiles that are defined outside the texture boundaries.
</description>
</method>
<method name="create_alternative_tile">
<return type="int" />
<argument index="0" name="atlas_coords" type="Vector2i" />
@ -124,6 +118,16 @@
Returns a tile's texture region in the atlas texture. For animated tiles, a [code]frame[/code] argument might be provided for the different frames of the animation.
</description>
</method>
<method name="get_tiles_to_be_removed_on_change">
<return type="PackedVector2Array" />
<argument index="0" name="texture" type="Texture2D" />
<argument index="1" name="margins" type="Vector2i" />
<argument index="2" name="separation" type="Vector2i" />
<argument index="3" name="texture_region_size" type="Vector2i" />
<description>
Returns an array of tiles coordinates ID that will be automatically removed when modifying one or several of those properties: [code]texture[/code], [code]margins[/code], [code]separation[/code] or [code]texture_region_size[/code]. This can be used to undo changes that would have caused tiles data loss.
</description>
</method>
<method name="has_room_for_tile" qualifiers="const">
<return type="bool" />
<argument index="0" name="atlas_coords" type="Vector2i" />
@ -136,12 +140,6 @@
Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [code]ignored_tile[/code] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties.
</description>
</method>
<method name="has_tiles_outside_texture">
<return type="bool" />
<description>
Returns if this atlas has tiles outside of its texture.
</description>
</method>
<method name="move_tile_in_atlas">
<return type="void" />
<argument index="0" name="atlas_coords" type="Vector2i" />

View file

@ -97,15 +97,6 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
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;
}
@ -213,43 +204,56 @@ 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 separation = tile_set_atlas_source->get_separation();
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();
// Draw the texture where there is no tile.
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) == TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (rect.size.x > 0 && rect.size.y > 0) {
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
// 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);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Bottom
int bottom_border = margins.y + (grid_size.y * texture_region_size.y);
int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Left
rect.position = Vector2i(0, margins.y);
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
// Right.
int right_border = margins.x + (grid_size.x * texture_region_size.x);
int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
@ -258,12 +262,30 @@ void TileAtlasView::_draw_base_tiles() {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value.
int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords);
Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0));
Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0);
// Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
// Draw, the texture in the separation areas
if (separation.x > 0) {
Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
if (separation.y > 0) {
Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
}
}
}
}
}
@ -299,30 +321,6 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
}
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 == TileSetSource::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.
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
@ -453,7 +451,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
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();
@ -544,7 +541,6 @@ void TileAtlasView::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();
@ -660,12 +656,6 @@ TileAtlasView::TileAtlasView() {
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
base_tiles_dark = memnew(Control);
base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
base_tiles_drawing_root->add_child(base_tiles_dark);
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);

View file

@ -93,9 +93,6 @@ private:
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.
@ -124,7 +121,6 @@ public:
// 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;

View file

@ -520,9 +520,6 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() {
void TileSetAtlasSourceEditor::_update_source_inspector() {
// Update the proxy object.
atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
// Update the "clear outside texture" button.
tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture());
}
void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
@ -1600,9 +1597,6 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
undo_redo->commit_action();
_update_tile_id_label();
} break;
case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: {
tile_set_atlas_source->clear_tiles_outside_texture();
} break;
case ADVANCED_AUTO_CREATE_TILES: {
_auto_create_tiles();
} break;
@ -2035,30 +2029,66 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
if (tile_data) {
undo_redo->start_force_keep_in_merge_ends();
AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
if (tile_data_proxy) {
Vector<String> components = String(p_property).split("/", true, 2);
if (components.size() == 2 && components[1] == "polygons_count") {
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
int new_polygons_count = p_new_value;
int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index));
int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index));
if (new_polygons_count < old_polygons_count) {
for (int i = new_polygons_count - 1; i < old_polygons_count; i++) {
ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
}
}
} else if (p_property == "terrain_set") {
int current_terrain_set = tile_data->get("terrain_set");
int current_terrain_set = tile_data_proxy->get("terrain_set");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
}
}
}
}
TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
if (atlas_source_proxy) {
TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
ERR_FAIL_COND(!atlas_source);
PackedVector2Array arr;
if (p_property == "texture") {
arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
} else if (p_property == "margins") {
arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size());
} else if (p_property == "separation") {
arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size());
} else if (p_property == "texture_region_size") {
arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value);
}
if (!arr.is_empty()) {
// Get all properties assigned to a tile.
List<PropertyInfo> properties;
atlas_source->get_property_list(&properties);
for (int i = 0; i < arr.size(); i++) {
Vector2i coords = arr[i];
String prefix = vformat("%d:%d/", coords.x, coords.y);
for (PropertyInfo pi : properties) {
if (pi.name.begins_with(prefix)) {
ADD_UNDO(atlas_source, pi.name);
}
}
}
}
}
undo_redo->end_force_keep_in_merge_ends();
#undef ADD_UNDO
}
@ -2406,8 +2436,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tool_advanced_menu_buttom = memnew(MenuButton);
tool_advanced_menu_buttom->set_flat(true);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE);
tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES);
tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
@ -2481,6 +2509,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view_missing_source_label->hide();
right_panel->add_child(tile_atlas_view_missing_source_label);
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback));
}
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {

View file

@ -78,6 +78,7 @@ private:
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
TileSetAtlasSource *get_edited() { return tile_set_atlas_source; };
};
// -- Proxy object for a tile, needed by the inspector --
@ -189,7 +190,6 @@ private:
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
};

View file

@ -3066,6 +3066,7 @@ void TileSetAtlasSource::reset_state() {
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
texture = p_texture;
_clear_tiles_outside_texture();
emit_changed();
}
@ -3081,6 +3082,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
_clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_margins() const {
@ -3095,6 +3097,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
_clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_separation() const {
@ -3109,6 +3112,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
_clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_texture_region_size() const {
@ -3354,7 +3358,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1);
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover.");
ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover.");
// Initialize the tile data.
TileAlternativesData tad;
@ -3552,9 +3556,7 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return false;
}
if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) {
if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) {
return false; // Only accept tiles outside the atlas if they are part of the ignored tile.
}
return false;
}
}
}
@ -3562,6 +3564,33 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return true;
}
PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) {
// Compute the new atlas grid size.
Size2 new_grid_size;
if (p_texture.is_valid()) {
Size2i valid_area = p_texture->get_size() - p_margins;
// Compute the number of valid tiles in the tiles atlas
if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) {
valid_area -= p_texture_region_size;
new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation);
}
}
Vector<Vector2> output;
for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) {
for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) {
Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0));
frame_coords += E.value.size_in_atlas;
if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) {
output.push_back(E.key);
break;
}
}
}
return output;
}
Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
@ -3626,34 +3655,6 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
emit_signal(SNAME("changed"));
}
bool TileSetAtlasSource::has_tiles_outside_texture() {
Vector2i grid_size = get_atlas_grid_size();
Vector<Vector2i> to_remove;
for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
return true;
}
}
return false;
}
void TileSetAtlasSource::clear_tiles_outside_texture() {
Vector2i grid_size = get_atlas_grid_size();
Vector<Vector2i> to_remove;
for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
to_remove.append(E.key);
}
}
for (int i = 0; i < to_remove.size(); i++) {
remove_tile(to_remove[i]);
}
}
int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
@ -3754,7 +3755,7 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS));
ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change);
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
@ -3779,8 +3780,6 @@ void TileSetAtlasSource::_bind_methods() {
// Helpers.
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
}
@ -3854,6 +3853,20 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
}
}
void TileSetAtlasSource::_clear_tiles_outside_texture() {
LocalVector<Vector2i> to_remove;
for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
to_remove.push_back(E.key);
}
}
for (unsigned int i = 0; i < to_remove.size(); i++) {
remove_tile(to_remove[i]);
}
}
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {

View file

@ -479,8 +479,10 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
void _create_coords_mapping_cache(Vector2i p_atlas_coords);
void _clear_tiles_outside_texture();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -534,7 +536,7 @@ public:
virtual Vector2i get_tile_id(int p_index) const override;
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
// Animation.
@ -565,8 +567,6 @@ public:
// 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, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;