Path: blob/master/editor/scene/2d/tiles/tile_atlas_view.cpp
9912 views
/**************************************************************************/1/* tile_atlas_view.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tile_atlas_view.h"3132#include "editor/settings/editor_settings.h"33#include "editor/themes/editor_scale.h"34#include "scene/2d/tile_map_layer.h"35#include "scene/gui/box_container.h"36#include "scene/gui/label.h"37#include "scene/gui/panel.h"38#include "scene/gui/view_panner.h"3940void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {41if (panner->gui_input(p_event, get_global_rect())) {42accept_event();43}44}4546void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {47panning += p_scroll_vec;48_update_zoom_and_panning(true);49emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);50}5152void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {53zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);54_update_zoom_and_panning(true, p_origin);55emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);56}5758Size2i TileAtlasView::_compute_base_tiles_control_size() {59if (tile_set_atlas_source.is_null()) {60return Size2i();61}62// Update the texture.63Vector2i size;64Ref<Texture2D> texture = tile_set_atlas_source->get_texture();65if (texture.is_valid()) {66size = texture->get_size();67}68return size;69}7071Size2i TileAtlasView::_compute_alternative_tiles_control_size() {72if (tile_set_atlas_source.is_null()) {73return Size2i();74}75Vector2i size;76for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {77Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);78int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);79Vector2i line_size;80Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;81for (int j = 1; j < alternatives_count; j++) {82int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);83bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();84line_size.x += transposed ? texture_region_size.y : texture_region_size.x;85line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);86}87size.x = MAX(size.x, line_size.x);88size.y += line_size.y;89}9091return size;92}9394void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos, const Vector2 &p_mouse_pos) {95if (tile_set_atlas_source.is_null()) {96return;97}98float zoom = zoom_widget->get_zoom();99100// Compute the minimum sizes.101Size2i base_tiles_control_size = _compute_base_tiles_control_size();102base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom);103104Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();105alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom);106107// Set the texture for the base tiles.108Ref<Texture2D> texture = tile_set_atlas_source->get_texture();109110// Set the scales.111if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {112base_tiles_drawing_root->set_scale(Vector2(zoom, zoom));113} else {114base_tiles_drawing_root->set_scale(Vector2(1, 1));115}116if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {117alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom));118} else {119alternative_tiles_drawing_root->set_scale(Vector2(1, 1));120}121122// Update the margin container's margins.123const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };124for (int i = 0; i < 4; i++) {125margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom);126}127128// Update the backgrounds.129background_left->set_size(base_tiles_root_control->get_custom_minimum_size());130background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());131132// Zoom on the position.133if (p_zoom_on_mouse_pos) {134Vector2 relative_mpos = p_mouse_pos - get_size() / 2;135panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;136} else {137// Center of panel.138panning = panning * zoom / previous_zoom;139}140button_center_view->set_disabled(panning.is_zero_approx());141142previous_zoom = zoom;143144center_container->set_begin(panning - center_container->get_minimum_size() / 2);145center_container->set_size(center_container->get_minimum_size());146}147148void TileAtlasView::_zoom_widget_changed() {149_update_zoom_and_panning();150emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);151}152153void TileAtlasView::_center_view() {154panning = Vector2();155button_center_view->set_disabled(true);156_update_zoom_and_panning();157emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);158}159160void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {161if (tile_set_atlas_source.is_null()) {162return;163}164base_tiles_root_control->set_tooltip_text("");165166Ref<InputEventMouseMotion> mm = p_event;167if (mm.is_valid()) {168Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();169Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));170if (coords != TileSetSource::INVALID_ATLAS_COORDS) {171coords = tile_set_atlas_source->get_tile_at_coords(coords);172if (coords != TileSetSource::INVALID_ATLAS_COORDS) {173base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));174}175}176}177}178179void TileAtlasView::_draw_base_tiles() {180if (tile_set.is_null() || tile_set_atlas_source.is_null()) {181return;182}183Ref<Texture2D> texture = tile_set_atlas_source->get_texture();184if (texture.is_valid()) {185Vector2i margins = tile_set_atlas_source->get_margins();186Vector2i separation = tile_set_atlas_source->get_separation();187Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();188Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();189190// Draw the texture where there is no tile.191for (int x = 0; x < grid_size.x; x++) {192for (int y = 0; y < grid_size.y; y++) {193Vector2i coords = Vector2i(x, y);194if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {195Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);196rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));197if (rect.size.x > 0 && rect.size.y > 0) {198base_tiles_draw->draw_texture_rect_region(texture, rect, rect);199}200}201}202}203204// Draw dark overlay after for performance reasons.205for (int x = 0; x < grid_size.x; x++) {206for (int y = 0; y < grid_size.y; y++) {207Vector2i coords = Vector2i(x, y);208if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {209Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);210rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));211if (rect.size.x > 0 && rect.size.y > 0) {212base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));213}214}215}216}217218// Draw the texture around the grid.219Rect2i rect;220221// Top.222rect.position = Vector2i();223rect.set_end(Vector2i(texture->get_size().x, margins.y));224base_tiles_draw->draw_texture_rect_region(texture, rect, rect);225base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));226227// Bottom228int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));229if (bottom_border < texture->get_size().y) {230rect.position = Vector2i(0, bottom_border);231rect.set_end(texture->get_size());232base_tiles_draw->draw_texture_rect_region(texture, rect, rect);233base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));234}235236// Left237rect.position = Vector2i(0, margins.y);238rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));239base_tiles_draw->draw_texture_rect_region(texture, rect, rect);240base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));241242// Right.243int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));244if (right_border < texture->get_size().x) {245rect.position = Vector2i(right_border, margins.y);246rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));247base_tiles_draw->draw_texture_rect_region(texture, rect, rect);248base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));249}250251// Draw actual tiles, using their properties (modulation, etc...)252for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {253Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);254255// Different materials need to be drawn with different CanvasItems.256RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);257258for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {259// Update the y to max value.260Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);261Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());262263// Draw the tile.264TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);265}266}267268// Draw Dark overlay on separation in its own pass.269if (separation.x > 0 || separation.y > 0) {270for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {271Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);272273for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {274// Update the y to max value.275Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);276277if (separation.x > 0) {278Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));279right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));280if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {281//base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);282base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));283}284}285286if (separation.y > 0) {287Rect2i 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));288bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));289if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {290//base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);291base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));292}293}294}295}296}297}298}299300RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {301Ref<Material> mat = p_for_data->get_material();302if (mat.is_null()) {303return p_base_item->get_canvas_item();304} else if (p_material_map.has(mat)) {305return p_material_map[mat];306} else {307RID ci_rid = RS::get_singleton()->canvas_item_create();308RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());309RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());310RS::get_singleton()->canvas_item_set_default_texture_filter(ci_rid, RS::CanvasItemTextureFilter(p_base_item->get_texture_filter_in_tree()));311p_material_map[mat] = ci_rid;312return ci_rid;313}314}315316void TileAtlasView::_clear_material_canvas_items() {317for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {318RS::get_singleton()->free(kv.value);319}320material_tiles_draw.clear();321322for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {323RS::get_singleton()->free(kv.value);324}325material_alternatives_draw.clear();326}327328void TileAtlasView::_draw_base_tiles_texture_grid() {329if (tile_set_atlas_source.is_null()) {330return;331}332Ref<Texture2D> texture = tile_set_atlas_source->get_texture();333if (texture.is_valid()) {334Vector2i margins = tile_set_atlas_source->get_margins();335Vector2i separation = tile_set_atlas_source->get_separation();336Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();337338Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();339340// Draw each tile texture region.341for (int x = 0; x < grid_size.x; x++) {342for (int y = 0; y < grid_size.y; y++) {343Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));344Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));345if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {346if (base_tile_coords == Vector2i(x, y)) {347// Draw existing tile.348Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);349Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));350base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);351}352} else {353// Draw the grid.354base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);355}356}357}358}359}360361void TileAtlasView::_draw_base_tiles_shape_grid() {362if (tile_set.is_null() || tile_set_atlas_source.is_null()) {363return;364}365// Draw the shapes.366Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");367Vector2i tile_shape_size = tile_set->get_tile_size();368for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {369Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);370Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();371if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {372for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {373Color color = grid_color;374if (frame > 0) {375color.a *= 0.3;376}377Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame);378Transform2D tile_xform;379tile_xform.set_origin(Rect2(texture_region).get_center() + in_tile_base_offset);380tile_xform.set_scale(tile_shape_size);381tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);382}383}384}385}386387void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {388alternative_tiles_root_control->set_tooltip_text("");389390Ref<InputEventMouseMotion> mm = p_event;391if (mm.is_valid()) {392Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();393Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));394Vector2i coords = Vector2i(coords3.x, coords3.y);395int alternative_id = coords3.z;396if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {397alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));398}399}400}401402void TileAtlasView::_draw_alternatives() {403if (tile_set.is_null() || tile_set_atlas_source.is_null()) {404return;405}406// Draw the alternative tiles.407Ref<Texture2D> texture = tile_set_atlas_source->get_texture();408if (texture.is_valid()) {409Vector2 current_pos;410for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {411Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);412current_pos.x = 0;413int y_increment = 0;414Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;415int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);416for (int j = 1; j < alternatives_count; j++) {417int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);418TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);419bool transposed = tile_data->get_transpose();420421// Different materials need to be drawn with different CanvasItems.422RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);423424// Update the y to max value.425Vector2i offset_pos;426if (transposed) {427offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin());428y_increment = MAX(y_increment, texture_region_size.x);429} else {430offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin());431y_increment = MAX(y_increment, texture_region_size.y);432}433434// Draw the tile.435TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);436437// Increment the x position.438current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;439}440if (alternatives_count > 1) {441current_pos.y += y_increment;442}443}444}445}446447void TileAtlasView::_draw_background_left() {448background_left->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_left->get_size()), true);449}450451void TileAtlasView::_draw_background_right() {452background_right->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_right->get_size()), true);453}454455void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {456tile_set = Ref<TileSet>(p_tile_set);457tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);458459_clear_material_canvas_items();460461if (tile_set.is_null()) {462return;463}464465ERR_FAIL_COND(p_source_id < 0);466ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);467468source_id = p_source_id;469470// Show or hide the view.471bool valid = tile_set_atlas_source->get_texture().is_valid();472hbox->set_visible(valid);473missing_source_label->set_visible(!valid);474475// Update the rect cache.476_update_alternative_tiles_rect_cache();477478// Update everything.479_update_zoom_and_panning();480481base_tiles_drawing_root->set_size(_compute_base_tiles_control_size());482alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size());483484// Update.485base_tiles_draw->queue_redraw();486base_tiles_texture_grid->queue_redraw();487base_tiles_shape_grid->queue_redraw();488alternatives_draw->queue_redraw();489background_left->queue_redraw();490background_right->queue_redraw();491}492493float TileAtlasView::get_zoom() const {494return zoom_widget->get_zoom();495}496497void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) {498zoom_widget->set_zoom(p_zoom);499panning = p_panning;500_update_zoom_and_panning();501}502503void TileAtlasView::set_padding(Side p_side, int p_padding) {504ERR_FAIL_COND(p_padding < 0);505margin_container_paddings[p_side] = p_padding;506}507508Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {509if (tile_set_atlas_source.is_null()) {510return Vector2i();511}512513Ref<Texture2D> texture = tile_set_atlas_source->get_texture();514if (texture.is_null()) {515return TileSetSource::INVALID_ATLAS_COORDS;516}517518Vector2i margins = tile_set_atlas_source->get_margins();519Vector2i separation = tile_set_atlas_source->get_separation();520Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();521522// Compute index in atlas.523Vector2 pos = p_pos - margins;524Vector2i ret = (pos / (texture_region_size + separation)).floor();525526// Clamp.527if (p_clamp) {528Vector2i size = tile_set_atlas_source->get_atlas_grid_size();529ret = ret.clamp(Vector2i(), size - Vector2i(1, 1));530}531532return ret;533}534535void TileAtlasView::_update_alternative_tiles_rect_cache() {536if (tile_set_atlas_source.is_null()) {537return;538}539540alternative_tiles_rect_cache.clear();541542Rect2i current;543for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {544Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);545int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);546Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;547int line_height = 0;548for (int j = 1; j < alternatives_count; j++) {549int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);550TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);551bool transposed = tile_data->get_transpose();552current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;553554// Update the rect.555if (!alternative_tiles_rect_cache.has(tile_id)) {556alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>();557}558alternative_tiles_rect_cache[tile_id][alternative_id] = current;559560current.position.x += transposed ? texture_region_size.y : texture_region_size.x;561line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);562}563564current.position.x = 0;565current.position.y += line_height;566}567}568569Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {570for (const KeyValue<Vector2, HashMap<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {571for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {572if (E_alternative.value.has_point(p_pos)) {573return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);574}575}576}577578return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);579}580581Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {582ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));583ERR_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));584585return alternative_tiles_rect_cache[p_coords][p_alternative_tile];586}587588void TileAtlasView::queue_redraw() {589base_tiles_draw->queue_redraw();590base_tiles_texture_grid->queue_redraw();591base_tiles_shape_grid->queue_redraw();592alternatives_draw->queue_redraw();593background_left->queue_redraw();594background_right->queue_redraw();595}596597void TileAtlasView::_update_theme_item_cache() {598Control::_update_theme_item_cache();599600theme_cache.center_view_icon = get_editor_theme_icon(SNAME("CenterView"));601theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));602}603604void TileAtlasView::_notification(int p_what) {605switch (p_what) {606case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {607if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {608break;609}610[[fallthrough]];611}612case NOTIFICATION_ENTER_TREE: {613panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));614panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));615} break;616617case NOTIFICATION_THEME_CHANGED: {618button_center_view->set_button_icon(theme_cache.center_view_icon);619} break;620}621}622623void TileAtlasView::_bind_methods() {624ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));625}626627TileAtlasView::TileAtlasView() {628set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);629630Panel *panel = memnew(Panel);631panel->set_clip_contents(true);632panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);633panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);634panel->set_h_size_flags(SIZE_EXPAND_FILL);635panel->set_v_size_flags(SIZE_EXPAND_FILL);636add_child(panel);637638zoom_widget = memnew(EditorZoomWidget);639add_child(zoom_widget);640zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);641zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));642zoom_widget->set_shortcut_context(this);643644button_center_view = memnew(Button);645button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);646button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);647button_center_view->connect(SceneStringName(pressed), callable_mp(this, &TileAtlasView::_center_view));648button_center_view->set_flat(true);649button_center_view->set_disabled(true);650button_center_view->set_tooltip_text(TTR("Center View"));651add_child(button_center_view);652653panner.instantiate();654panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));655panner->set_enable_rmb(true);656657center_container = memnew(CenterContainer);658center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);659center_container->set_anchors_preset(Control::PRESET_CENTER);660center_container->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::gui_input));661center_container->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key));662center_container->set_focus_mode(FOCUS_CLICK);663panel->add_child(center_container);664665missing_source_label = memnew(Label);666missing_source_label->set_focus_mode(FOCUS_ACCESSIBILITY);667missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));668center_container->add_child(missing_source_label);669670margin_container = memnew(MarginContainer);671margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);672center_container->add_child(margin_container);673674hbox = memnew(HBoxContainer);675hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);676hbox->add_theme_constant_override("separation", 10);677hbox->hide();678margin_container->add_child(hbox);679680VBoxContainer *left_vbox = memnew(VBoxContainer);681left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);682hbox->add_child(left_vbox);683684VBoxContainer *right_vbox = memnew(VBoxContainer);685right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);686hbox->add_child(right_vbox);687688// Base tiles.689Label *base_tile_label = memnew(Label);690base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);691base_tile_label->set_text(TTR("Base Tiles"));692base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);693left_vbox->add_child(base_tile_label);694695base_tiles_root_control = memnew(Control);696base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);697base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);698base_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));699left_vbox->add_child(base_tiles_root_control);700701background_left = memnew(Control);702background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);703background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);704background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);705background_left->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_left));706base_tiles_root_control->add_child(background_left);707708base_tiles_drawing_root = memnew(Control);709base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);710base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);711base_tiles_root_control->add_child(base_tiles_drawing_root);712713base_tiles_draw = memnew(Control);714base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);715base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);716base_tiles_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles));717base_tiles_drawing_root->add_child(base_tiles_draw);718719base_tiles_texture_grid = memnew(Control);720base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);721base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);722base_tiles_texture_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));723base_tiles_drawing_root->add_child(base_tiles_texture_grid);724725base_tiles_shape_grid = memnew(Control);726base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);727base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);728base_tiles_shape_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));729base_tiles_drawing_root->add_child(base_tiles_shape_grid);730731// Alternative tiles.732Label *alternative_tiles_label = memnew(Label);733alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);734alternative_tiles_label->set_text(TTR("Alternative Tiles"));735alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);736right_vbox->add_child(alternative_tiles_label);737738alternative_tiles_root_control = memnew(Control);739alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);740alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);741alternative_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));742right_vbox->add_child(alternative_tiles_root_control);743744background_right = memnew(Control);745background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);746background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);747background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);748background_right->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_right));749alternative_tiles_root_control->add_child(background_right);750751alternative_tiles_drawing_root = memnew(Control);752alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);753alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);754alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);755756alternatives_draw = memnew(Control);757alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);758alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);759alternatives_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_alternatives));760alternative_tiles_drawing_root->add_child(alternatives_draw);761}762763TileAtlasView::~TileAtlasView() {764_clear_material_canvas_items();765}766767768