Merge pull request #54485 from groud/atlas_texture_padding
This commit is contained in:
commit
0ba215642d
5 changed files with 171 additions and 6 deletions
|
@ -45,6 +45,21 @@
|
|||
Returns the alternative ID a following call to [method create_alternative_tile] would return.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_runtime_texture" qualifiers="const">
|
||||
<return type="Texture2D" />
|
||||
<description>
|
||||
If [member use_texture_padding] is [code]false[/code], returns [member texture]. Otherwise, returns and internal [ImageTexture] created that includes the padding.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_runtime_tile_texture_region" qualifiers="const">
|
||||
<return type="Rect2i" />
|
||||
<argument index="0" name="atlas_coords" type="Vector2i" />
|
||||
<argument index="1" name="frame" type="int" />
|
||||
<description>
|
||||
Returns the region of the tile at coordinates [code]atlas_coords[/code] for frame [code]frame[/code] inside the texture returned by [method get_runtime_texture].
|
||||
[b]Note:[/b] If [member use_texture_padding] is [code]false[/code], returns the same as [method get_tile_texture_region].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_tile_animation_columns" qualifiers="const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="atlas_coords" type="Vector2i" />
|
||||
|
@ -232,5 +247,9 @@
|
|||
<member name="texture_region_size" type="Vector2i" setter="set_texture_region_size" getter="get_texture_region_size" default="Vector2i(16, 16)">
|
||||
The base tile size in the texture (in pixel). This size must be bigger than the TileSet's [code]tile_size[/code] value.
|
||||
</member>
|
||||
<member name="use_texture_padding" type="bool" setter="set_use_texture_padding" getter="get_use_texture_padding" default="true">
|
||||
If [code]true[/code], generates an internal texture with an additional one pixel padding around each tile. Texture padding avoids a common artifact where lines appear between tiles.
|
||||
Disabling this setting might lead a small performance improvement, as generating the internal texture requires both memory and processing time when the TileSetAtlasSource resource is modified.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
|
|
@ -99,6 +99,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list
|
|||
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, ""));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, ""));
|
||||
}
|
||||
|
||||
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() {
|
||||
|
|
|
@ -1259,7 +1259,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
|
|||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||
if (atlas_source) {
|
||||
Vector2i grid_size = atlas_source->get_atlas_grid_size();
|
||||
if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
|
||||
if (!atlas_source->get_runtime_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
|
||||
// Generate a random color from the hashed values of the tiles.
|
||||
Array to_hash;
|
||||
to_hash.push_back(c.source_id);
|
||||
|
@ -1299,7 +1299,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
|
|||
}
|
||||
|
||||
// Get the texture.
|
||||
Ref<Texture2D> tex = atlas_source->get_texture();
|
||||
Ref<Texture2D> tex = atlas_source->get_runtime_texture();
|
||||
if (!tex.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1321,7 +1321,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
|
|||
|
||||
// Get destination rect.
|
||||
Rect2 dest_rect;
|
||||
dest_rect.size = atlas_source->get_tile_texture_region(p_atlas_coords).size;
|
||||
dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
|
||||
dest_rect.size.x += FP_ADJUST;
|
||||
dest_rect.size.y += FP_ADJUST;
|
||||
|
||||
|
@ -1342,10 +1342,10 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
|
|||
|
||||
// Draw the tile.
|
||||
if (p_frame >= 0) {
|
||||
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, p_frame);
|
||||
Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
|
||||
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
|
||||
} else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
|
||||
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, 0);
|
||||
Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
|
||||
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
|
||||
} else {
|
||||
real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
|
||||
|
@ -1355,7 +1355,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
|
|||
real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed;
|
||||
RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, 0.0);
|
||||
|
||||
Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, frame);
|
||||
Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
|
||||
tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
|
||||
|
||||
time += frame_duration;
|
||||
|
|
|
@ -3526,9 +3526,18 @@ void TileSetAtlasSource::reset_state() {
|
|||
}
|
||||
|
||||
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
|
||||
if (texture.is_valid()) {
|
||||
texture->disconnect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
|
||||
}
|
||||
|
||||
texture = p_texture;
|
||||
|
||||
if (texture.is_valid()) {
|
||||
texture->connect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
|
||||
}
|
||||
|
||||
_clear_tiles_outside_texture();
|
||||
_queue_update_padded_texture();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
|
@ -3545,8 +3554,10 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
|
|||
}
|
||||
|
||||
_clear_tiles_outside_texture();
|
||||
_queue_update_padded_texture();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
Vector2i TileSetAtlasSource::get_margins() const {
|
||||
return margins;
|
||||
}
|
||||
|
@ -3560,8 +3571,10 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
|
|||
}
|
||||
|
||||
_clear_tiles_outside_texture();
|
||||
_queue_update_padded_texture();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
Vector2i TileSetAtlasSource::get_separation() const {
|
||||
return separation;
|
||||
}
|
||||
|
@ -3575,12 +3588,27 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
|
|||
}
|
||||
|
||||
_clear_tiles_outside_texture();
|
||||
_queue_update_padded_texture();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
Vector2i TileSetAtlasSource::get_texture_region_size() const {
|
||||
return texture_region_size;
|
||||
}
|
||||
|
||||
void TileSetAtlasSource::set_use_texture_padding(bool p_use_padding) {
|
||||
if (use_texture_padding == p_use_padding) {
|
||||
return;
|
||||
}
|
||||
use_texture_padding = p_use_padding;
|
||||
_queue_update_padded_texture();
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
bool TileSetAtlasSource::get_use_texture_padding() const {
|
||||
return use_texture_padding;
|
||||
}
|
||||
|
||||
Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
|
||||
Ref<Texture2D> texture = get_texture();
|
||||
if (!texture.is_valid()) {
|
||||
|
@ -3839,6 +3867,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
|
|||
tiles_ids.sort();
|
||||
|
||||
_create_coords_mapping_cache(p_atlas_coords);
|
||||
_queue_update_padded_texture();
|
||||
|
||||
emit_signal(SNAME("changed"));
|
||||
}
|
||||
|
@ -3859,6 +3888,8 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
|
|||
tiles_ids.erase(p_atlas_coords);
|
||||
tiles_ids.sort();
|
||||
|
||||
_queue_update_padded_texture();
|
||||
|
||||
emit_signal(SNAME("changed"));
|
||||
}
|
||||
|
||||
|
@ -3887,6 +3918,7 @@ void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coord
|
|||
tiles[p_atlas_coords].animation_columns = p_frame_columns;
|
||||
|
||||
_create_coords_mapping_cache(p_atlas_coords);
|
||||
_queue_update_padded_texture();
|
||||
|
||||
emit_signal(SNAME("changed"));
|
||||
}
|
||||
|
@ -3909,6 +3941,7 @@ void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_co
|
|||
tiles[p_atlas_coords].animation_separation = p_separation;
|
||||
|
||||
_create_coords_mapping_cache(p_atlas_coords);
|
||||
_queue_update_padded_texture();
|
||||
|
||||
emit_signal(SNAME("changed"));
|
||||
}
|
||||
|
@ -3953,6 +3986,7 @@ void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_
|
|||
}
|
||||
|
||||
_create_coords_mapping_cache(p_atlas_coords);
|
||||
_queue_update_padded_texture();
|
||||
|
||||
notify_property_list_changed();
|
||||
|
||||
|
@ -4091,6 +4125,31 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
|
|||
return effective_texture_offset;
|
||||
}
|
||||
|
||||
// Getters for texture and tile region (padded or not)
|
||||
Ref<Texture2D> TileSetAtlasSource::get_runtime_texture() const {
|
||||
if (use_texture_padding) {
|
||||
return padded_texture;
|
||||
} else {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i TileSetAtlasSource::get_runtime_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());
|
||||
|
||||
Rect2i src_rect = get_tile_texture_region(p_atlas_coords, p_frame);
|
||||
if (use_texture_padding) {
|
||||
const TileAlternativesData &tad = tiles[p_atlas_coords];
|
||||
Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0));
|
||||
Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
|
||||
|
||||
return Rect2i(base_pos, src_rect.size);
|
||||
} else {
|
||||
return src_rect;
|
||||
}
|
||||
}
|
||||
|
||||
void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
|
||||
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
||||
|
||||
|
@ -4121,6 +4180,7 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
|
|||
tiles[new_atlas_coords].size_in_atlas = new_size;
|
||||
|
||||
_create_coords_mapping_cache(new_atlas_coords);
|
||||
_queue_update_padded_texture();
|
||||
|
||||
emit_signal(SNAME("changed"));
|
||||
}
|
||||
|
@ -4212,11 +4272,14 @@ void TileSetAtlasSource::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation);
|
||||
ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size);
|
||||
ClassDB::bind_method(D_METHOD("set_use_texture_padding", "use_texture_padding"), &TileSetAtlasSource::set_use_texture_padding);
|
||||
ClassDB::bind_method(D_METHOD("get_use_texture_padding"), &TileSetAtlasSource::get_use_texture_padding);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NO_EDITOR), "set_texture", "get_texture");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_texture_padding", "get_use_texture_padding");
|
||||
|
||||
// Base tiles
|
||||
ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
|
||||
|
@ -4251,6 +4314,11 @@ void TileSetAtlasSource::_bind_methods() {
|
|||
// Helpers.
|
||||
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
|
||||
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
|
||||
|
||||
// Getters for texture and tile region (padded or not)
|
||||
ClassDB::bind_method(D_METHOD("_update_padded_texture"), &TileSetAtlasSource::_update_padded_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_runtime_texture"), &TileSetAtlasSource::get_runtime_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_runtime_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_runtime_tile_texture_region);
|
||||
}
|
||||
|
||||
TileSetAtlasSource::~TileSetAtlasSource() {
|
||||
|
@ -4337,6 +4405,69 @@ void TileSetAtlasSource::_clear_tiles_outside_texture() {
|
|||
}
|
||||
}
|
||||
|
||||
void TileSetAtlasSource::_queue_update_padded_texture() {
|
||||
padded_texture_needs_update = true;
|
||||
call_deferred("_update_padded_texture");
|
||||
}
|
||||
|
||||
void TileSetAtlasSource::_update_padded_texture() {
|
||||
if (!padded_texture_needs_update) {
|
||||
return;
|
||||
}
|
||||
padded_texture_needs_update = false;
|
||||
padded_texture = Ref<ImageTexture>();
|
||||
|
||||
if (!texture.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!use_texture_padding) {
|
||||
return;
|
||||
}
|
||||
|
||||
Size2 size = get_atlas_grid_size() * (texture_region_size + Vector2i(2, 2));
|
||||
|
||||
Ref<Image> src = texture->get_image();
|
||||
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
image->create(size.x, size.y, false, Image::FORMAT_RGBA8);
|
||||
|
||||
for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) {
|
||||
for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) {
|
||||
// Compute the source rects.
|
||||
Rect2i src_rect = get_tile_texture_region(kv.key, frame);
|
||||
|
||||
Rect2i top_src_rect = Rect2i(src_rect.position, Vector2i(src_rect.size.x, 1));
|
||||
Rect2i bottom_src_rect = Rect2i(src_rect.position + Vector2i(0, src_rect.size.y - 1), Vector2i(src_rect.size.x, 1));
|
||||
Rect2i left_src_rect = Rect2i(src_rect.position, Vector2i(1, src_rect.size.y));
|
||||
Rect2i right_src_rect = Rect2i(src_rect.position + Vector2i(src_rect.size.x - 1, 0), Vector2i(1, src_rect.size.y));
|
||||
|
||||
// Copy the tile and the paddings.
|
||||
Vector2i frame_coords = kv.key + (kv.value.size_in_atlas + kv.value.animation_separation) * ((kv.value.animation_columns > 0) ? Vector2i(frame % kv.value.animation_columns, frame / kv.value.animation_columns) : Vector2i(frame, 0));
|
||||
Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
|
||||
|
||||
image->blit_rect(*src, src_rect, base_pos);
|
||||
|
||||
image->blit_rect(*src, top_src_rect, base_pos + Vector2i(0, -1));
|
||||
image->blit_rect(*src, bottom_src_rect, base_pos + Vector2i(0, src_rect.size.y));
|
||||
image->blit_rect(*src, left_src_rect, base_pos + Vector2i(-1, 0));
|
||||
image->blit_rect(*src, right_src_rect, base_pos + Vector2i(src_rect.size.x, 0));
|
||||
|
||||
image->set_pixelv(base_pos + Vector2i(-1, -1), src->get_pixelv(src_rect.position));
|
||||
image->set_pixelv(base_pos + Vector2i(src_rect.size.x, -1), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, 0)));
|
||||
image->set_pixelv(base_pos + Vector2i(-1, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(0, src_rect.size.y - 1)));
|
||||
image->set_pixelv(base_pos + Vector2i(src_rect.size.x, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, src_rect.size.y - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!padded_texture.is_valid()) {
|
||||
padded_texture.instantiate();
|
||||
}
|
||||
padded_texture->create_from_image(image);
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
|
||||
|
||||
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
|
||||
|
|
|
@ -603,6 +603,12 @@ private:
|
|||
|
||||
void _clear_tiles_outside_texture();
|
||||
|
||||
bool use_texture_padding = true;
|
||||
Ref<ImageTexture> padded_texture;
|
||||
bool padded_texture_needs_update = false;
|
||||
void _queue_update_padded_texture();
|
||||
void _update_padded_texture();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
@ -645,6 +651,10 @@ public:
|
|||
void set_texture_region_size(Vector2i p_tile_size);
|
||||
Vector2i get_texture_region_size() const;
|
||||
|
||||
// Padding.
|
||||
void set_use_texture_padding(bool p_use_padding);
|
||||
bool get_use_texture_padding() const;
|
||||
|
||||
// Base tiles.
|
||||
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
|
||||
void remove_tile(Vector2i p_atlas_coords);
|
||||
|
@ -690,6 +700,10 @@ public:
|
|||
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;
|
||||
|
||||
// Getters for texture and tile region (padded or not)
|
||||
Ref<Texture2D> get_runtime_texture() const;
|
||||
Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
||||
|
||||
~TileSetAtlasSource();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue