Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/2d/tiles/tile_atlas_view.cpp
9912 views
1
/**************************************************************************/
2
/* tile_atlas_view.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "tile_atlas_view.h"
32
33
#include "editor/settings/editor_settings.h"
34
#include "editor/themes/editor_scale.h"
35
#include "scene/2d/tile_map_layer.h"
36
#include "scene/gui/box_container.h"
37
#include "scene/gui/label.h"
38
#include "scene/gui/panel.h"
39
#include "scene/gui/view_panner.h"
40
41
void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
42
if (panner->gui_input(p_event, get_global_rect())) {
43
accept_event();
44
}
45
}
46
47
void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
48
panning += p_scroll_vec;
49
_update_zoom_and_panning(true);
50
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
51
}
52
53
void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
54
zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
55
_update_zoom_and_panning(true, p_origin);
56
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
57
}
58
59
Size2i TileAtlasView::_compute_base_tiles_control_size() {
60
if (tile_set_atlas_source.is_null()) {
61
return Size2i();
62
}
63
// Update the texture.
64
Vector2i size;
65
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
66
if (texture.is_valid()) {
67
size = texture->get_size();
68
}
69
return size;
70
}
71
72
Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
73
if (tile_set_atlas_source.is_null()) {
74
return Size2i();
75
}
76
Vector2i size;
77
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
78
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
79
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
80
Vector2i line_size;
81
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
82
for (int j = 1; j < alternatives_count; j++) {
83
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
84
bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();
85
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
86
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
87
}
88
size.x = MAX(size.x, line_size.x);
89
size.y += line_size.y;
90
}
91
92
return size;
93
}
94
95
void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos, const Vector2 &p_mouse_pos) {
96
if (tile_set_atlas_source.is_null()) {
97
return;
98
}
99
float zoom = zoom_widget->get_zoom();
100
101
// Compute the minimum sizes.
102
Size2i base_tiles_control_size = _compute_base_tiles_control_size();
103
base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom);
104
105
Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
106
alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom);
107
108
// Set the texture for the base tiles.
109
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
110
111
// Set the scales.
112
if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
113
base_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
114
} else {
115
base_tiles_drawing_root->set_scale(Vector2(1, 1));
116
}
117
if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
118
alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom));
119
} else {
120
alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
121
}
122
123
// Update the margin container's margins.
124
const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
125
for (int i = 0; i < 4; i++) {
126
margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom);
127
}
128
129
// Update the backgrounds.
130
background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
131
background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
132
133
// Zoom on the position.
134
if (p_zoom_on_mouse_pos) {
135
Vector2 relative_mpos = p_mouse_pos - get_size() / 2;
136
panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos;
137
} else {
138
// Center of panel.
139
panning = panning * zoom / previous_zoom;
140
}
141
button_center_view->set_disabled(panning.is_zero_approx());
142
143
previous_zoom = zoom;
144
145
center_container->set_begin(panning - center_container->get_minimum_size() / 2);
146
center_container->set_size(center_container->get_minimum_size());
147
}
148
149
void TileAtlasView::_zoom_widget_changed() {
150
_update_zoom_and_panning();
151
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
152
}
153
154
void TileAtlasView::_center_view() {
155
panning = Vector2();
156
button_center_view->set_disabled(true);
157
_update_zoom_and_panning();
158
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
159
}
160
161
void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
162
if (tile_set_atlas_source.is_null()) {
163
return;
164
}
165
base_tiles_root_control->set_tooltip_text("");
166
167
Ref<InputEventMouseMotion> mm = p_event;
168
if (mm.is_valid()) {
169
Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
170
Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
171
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
172
coords = tile_set_atlas_source->get_tile_at_coords(coords);
173
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
174
base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
175
}
176
}
177
}
178
}
179
180
void TileAtlasView::_draw_base_tiles() {
181
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
182
return;
183
}
184
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
185
if (texture.is_valid()) {
186
Vector2i margins = tile_set_atlas_source->get_margins();
187
Vector2i separation = tile_set_atlas_source->get_separation();
188
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
189
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
190
191
// Draw the texture where there is no tile.
192
for (int x = 0; x < grid_size.x; x++) {
193
for (int y = 0; y < grid_size.y; y++) {
194
Vector2i coords = Vector2i(x, y);
195
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
196
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
197
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
198
if (rect.size.x > 0 && rect.size.y > 0) {
199
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
200
}
201
}
202
}
203
}
204
205
// Draw dark overlay after for performance reasons.
206
for (int x = 0; x < grid_size.x; x++) {
207
for (int y = 0; y < grid_size.y; y++) {
208
Vector2i coords = Vector2i(x, y);
209
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
210
Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
211
rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
212
if (rect.size.x > 0 && rect.size.y > 0) {
213
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
214
}
215
}
216
}
217
}
218
219
// Draw the texture around the grid.
220
Rect2i rect;
221
222
// Top.
223
rect.position = Vector2i();
224
rect.set_end(Vector2i(texture->get_size().x, margins.y));
225
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
226
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
227
228
// Bottom
229
int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
230
if (bottom_border < texture->get_size().y) {
231
rect.position = Vector2i(0, bottom_border);
232
rect.set_end(texture->get_size());
233
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
234
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
235
}
236
237
// Left
238
rect.position = Vector2i(0, margins.y);
239
rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
240
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
241
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
242
243
// Right.
244
int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
245
if (right_border < texture->get_size().x) {
246
rect.position = Vector2i(right_border, margins.y);
247
rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
248
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
249
base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
250
}
251
252
// Draw actual tiles, using their properties (modulation, etc...)
253
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
254
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
255
256
// Different materials need to be drawn with different CanvasItems.
257
RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);
258
259
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
260
// Update the y to max value.
261
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
262
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
263
264
// Draw the tile.
265
TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
266
}
267
}
268
269
// Draw Dark overlay on separation in its own pass.
270
if (separation.x > 0 || separation.y > 0) {
271
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
272
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
273
274
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
275
// Update the y to max value.
276
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
277
278
if (separation.x > 0) {
279
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));
280
right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
281
if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
282
//base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
283
base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
284
}
285
}
286
287
if (separation.y > 0) {
288
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));
289
bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
290
if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
291
//base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
292
base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
293
}
294
}
295
}
296
}
297
}
298
}
299
}
300
301
RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {
302
Ref<Material> mat = p_for_data->get_material();
303
if (mat.is_null()) {
304
return p_base_item->get_canvas_item();
305
} else if (p_material_map.has(mat)) {
306
return p_material_map[mat];
307
} else {
308
RID ci_rid = RS::get_singleton()->canvas_item_create();
309
RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());
310
RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());
311
RS::get_singleton()->canvas_item_set_default_texture_filter(ci_rid, RS::CanvasItemTextureFilter(p_base_item->get_texture_filter_in_tree()));
312
p_material_map[mat] = ci_rid;
313
return ci_rid;
314
}
315
}
316
317
void TileAtlasView::_clear_material_canvas_items() {
318
for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {
319
RS::get_singleton()->free(kv.value);
320
}
321
material_tiles_draw.clear();
322
323
for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {
324
RS::get_singleton()->free(kv.value);
325
}
326
material_alternatives_draw.clear();
327
}
328
329
void TileAtlasView::_draw_base_tiles_texture_grid() {
330
if (tile_set_atlas_source.is_null()) {
331
return;
332
}
333
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
334
if (texture.is_valid()) {
335
Vector2i margins = tile_set_atlas_source->get_margins();
336
Vector2i separation = tile_set_atlas_source->get_separation();
337
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
338
339
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
340
341
// Draw each tile texture region.
342
for (int x = 0; x < grid_size.x; x++) {
343
for (int y = 0; y < grid_size.y; y++) {
344
Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
345
Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
346
if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
347
if (base_tile_coords == Vector2i(x, y)) {
348
// Draw existing tile.
349
Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
350
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
351
base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
352
}
353
} else {
354
// Draw the grid.
355
base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
356
}
357
}
358
}
359
}
360
}
361
362
void TileAtlasView::_draw_base_tiles_shape_grid() {
363
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
364
return;
365
}
366
// Draw the shapes.
367
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
368
Vector2i tile_shape_size = tile_set->get_tile_size();
369
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
370
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
371
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
372
if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
373
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
374
Color color = grid_color;
375
if (frame > 0) {
376
color.a *= 0.3;
377
}
378
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id, frame);
379
Transform2D tile_xform;
380
tile_xform.set_origin(Rect2(texture_region).get_center() + in_tile_base_offset);
381
tile_xform.set_scale(tile_shape_size);
382
tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
383
}
384
}
385
}
386
}
387
388
void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
389
alternative_tiles_root_control->set_tooltip_text("");
390
391
Ref<InputEventMouseMotion> mm = p_event;
392
if (mm.is_valid()) {
393
Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
394
Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
395
Vector2i coords = Vector2i(coords3.x, coords3.y);
396
int alternative_id = coords3.z;
397
if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
398
alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
399
}
400
}
401
}
402
403
void TileAtlasView::_draw_alternatives() {
404
if (tile_set.is_null() || tile_set_atlas_source.is_null()) {
405
return;
406
}
407
// Draw the alternative tiles.
408
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
409
if (texture.is_valid()) {
410
Vector2 current_pos;
411
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
412
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
413
current_pos.x = 0;
414
int y_increment = 0;
415
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
416
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
417
for (int j = 1; j < alternatives_count; j++) {
418
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
419
TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
420
bool transposed = tile_data->get_transpose();
421
422
// Different materials need to be drawn with different CanvasItems.
423
RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);
424
425
// Update the y to max value.
426
Vector2i offset_pos;
427
if (transposed) {
428
offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_data->get_texture_origin());
429
y_increment = MAX(y_increment, texture_region_size.x);
430
} else {
431
offset_pos = (current_pos + texture_region_size / 2 + tile_data->get_texture_origin());
432
y_increment = MAX(y_increment, texture_region_size.y);
433
}
434
435
// Draw the tile.
436
TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
437
438
// Increment the x position.
439
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
440
}
441
if (alternatives_count > 1) {
442
current_pos.y += y_increment;
443
}
444
}
445
}
446
}
447
448
void TileAtlasView::_draw_background_left() {
449
background_left->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_left->get_size()), true);
450
}
451
452
void TileAtlasView::_draw_background_right() {
453
background_right->draw_texture_rect(theme_cache.checkerboard, Rect2(Vector2(), background_right->get_size()), true);
454
}
455
456
void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
457
tile_set = Ref<TileSet>(p_tile_set);
458
tile_set_atlas_source = Ref<TileSetAtlasSource>(p_tile_set_atlas_source);
459
460
_clear_material_canvas_items();
461
462
if (tile_set.is_null()) {
463
return;
464
}
465
466
ERR_FAIL_COND(p_source_id < 0);
467
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
468
469
source_id = p_source_id;
470
471
// Show or hide the view.
472
bool valid = tile_set_atlas_source->get_texture().is_valid();
473
hbox->set_visible(valid);
474
missing_source_label->set_visible(!valid);
475
476
// Update the rect cache.
477
_update_alternative_tiles_rect_cache();
478
479
// Update everything.
480
_update_zoom_and_panning();
481
482
base_tiles_drawing_root->set_size(_compute_base_tiles_control_size());
483
alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size());
484
485
// Update.
486
base_tiles_draw->queue_redraw();
487
base_tiles_texture_grid->queue_redraw();
488
base_tiles_shape_grid->queue_redraw();
489
alternatives_draw->queue_redraw();
490
background_left->queue_redraw();
491
background_right->queue_redraw();
492
}
493
494
float TileAtlasView::get_zoom() const {
495
return zoom_widget->get_zoom();
496
}
497
498
void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) {
499
zoom_widget->set_zoom(p_zoom);
500
panning = p_panning;
501
_update_zoom_and_panning();
502
}
503
504
void TileAtlasView::set_padding(Side p_side, int p_padding) {
505
ERR_FAIL_COND(p_padding < 0);
506
margin_container_paddings[p_side] = p_padding;
507
}
508
509
Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const {
510
if (tile_set_atlas_source.is_null()) {
511
return Vector2i();
512
}
513
514
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
515
if (texture.is_null()) {
516
return TileSetSource::INVALID_ATLAS_COORDS;
517
}
518
519
Vector2i margins = tile_set_atlas_source->get_margins();
520
Vector2i separation = tile_set_atlas_source->get_separation();
521
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
522
523
// Compute index in atlas.
524
Vector2 pos = p_pos - margins;
525
Vector2i ret = (pos / (texture_region_size + separation)).floor();
526
527
// Clamp.
528
if (p_clamp) {
529
Vector2i size = tile_set_atlas_source->get_atlas_grid_size();
530
ret = ret.clamp(Vector2i(), size - Vector2i(1, 1));
531
}
532
533
return ret;
534
}
535
536
void TileAtlasView::_update_alternative_tiles_rect_cache() {
537
if (tile_set_atlas_source.is_null()) {
538
return;
539
}
540
541
alternative_tiles_rect_cache.clear();
542
543
Rect2i current;
544
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
545
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
546
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
547
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
548
int line_height = 0;
549
for (int j = 1; j < alternatives_count; j++) {
550
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
551
TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);
552
bool transposed = tile_data->get_transpose();
553
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
554
555
// Update the rect.
556
if (!alternative_tiles_rect_cache.has(tile_id)) {
557
alternative_tiles_rect_cache[tile_id] = HashMap<int, Rect2i>();
558
}
559
alternative_tiles_rect_cache[tile_id][alternative_id] = current;
560
561
current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
562
line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
563
}
564
565
current.position.x = 0;
566
current.position.y += line_height;
567
}
568
}
569
570
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
571
for (const KeyValue<Vector2, HashMap<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
572
for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
573
if (E_alternative.value.has_point(p_pos)) {
574
return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
575
}
576
}
577
}
578
579
return Vector3i(TileSetSource::INVALID_ATLAS_COORDS.x, TileSetSource::INVALID_ATLAS_COORDS.y, TileSetSource::INVALID_TILE_ALTERNATIVE);
580
}
581
582
Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
583
ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
584
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));
585
586
return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
587
}
588
589
void TileAtlasView::queue_redraw() {
590
base_tiles_draw->queue_redraw();
591
base_tiles_texture_grid->queue_redraw();
592
base_tiles_shape_grid->queue_redraw();
593
alternatives_draw->queue_redraw();
594
background_left->queue_redraw();
595
background_right->queue_redraw();
596
}
597
598
void TileAtlasView::_update_theme_item_cache() {
599
Control::_update_theme_item_cache();
600
601
theme_cache.center_view_icon = get_editor_theme_icon(SNAME("CenterView"));
602
theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
603
}
604
605
void TileAtlasView::_notification(int p_what) {
606
switch (p_what) {
607
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
608
if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
609
break;
610
}
611
[[fallthrough]];
612
}
613
case NOTIFICATION_ENTER_TREE: {
614
panner->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")));
615
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
616
} break;
617
618
case NOTIFICATION_THEME_CHANGED: {
619
button_center_view->set_button_icon(theme_cache.center_view_icon);
620
} break;
621
}
622
}
623
624
void TileAtlasView::_bind_methods() {
625
ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
626
}
627
628
TileAtlasView::TileAtlasView() {
629
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
630
631
Panel *panel = memnew(Panel);
632
panel->set_clip_contents(true);
633
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
634
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
635
panel->set_h_size_flags(SIZE_EXPAND_FILL);
636
panel->set_v_size_flags(SIZE_EXPAND_FILL);
637
add_child(panel);
638
639
zoom_widget = memnew(EditorZoomWidget);
640
add_child(zoom_widget);
641
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
642
zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
643
zoom_widget->set_shortcut_context(this);
644
645
button_center_view = memnew(Button);
646
button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);
647
button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);
648
button_center_view->connect(SceneStringName(pressed), callable_mp(this, &TileAtlasView::_center_view));
649
button_center_view->set_flat(true);
650
button_center_view->set_disabled(true);
651
button_center_view->set_tooltip_text(TTR("Center View"));
652
add_child(button_center_view);
653
654
panner.instantiate();
655
panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
656
panner->set_enable_rmb(true);
657
658
center_container = memnew(CenterContainer);
659
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
660
center_container->set_anchors_preset(Control::PRESET_CENTER);
661
center_container->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::gui_input));
662
center_container->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
663
center_container->set_focus_mode(FOCUS_CLICK);
664
panel->add_child(center_container);
665
666
missing_source_label = memnew(Label);
667
missing_source_label->set_focus_mode(FOCUS_ACCESSIBILITY);
668
missing_source_label->set_text(TTR("The selected atlas source has no valid texture. Assign a texture in the TileSet bottom tab."));
669
center_container->add_child(missing_source_label);
670
671
margin_container = memnew(MarginContainer);
672
margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
673
center_container->add_child(margin_container);
674
675
hbox = memnew(HBoxContainer);
676
hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
677
hbox->add_theme_constant_override("separation", 10);
678
hbox->hide();
679
margin_container->add_child(hbox);
680
681
VBoxContainer *left_vbox = memnew(VBoxContainer);
682
left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
683
hbox->add_child(left_vbox);
684
685
VBoxContainer *right_vbox = memnew(VBoxContainer);
686
right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
687
hbox->add_child(right_vbox);
688
689
// Base tiles.
690
Label *base_tile_label = memnew(Label);
691
base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
692
base_tile_label->set_text(TTR("Base Tiles"));
693
base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
694
left_vbox->add_child(base_tile_label);
695
696
base_tiles_root_control = memnew(Control);
697
base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
698
base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
699
base_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
700
left_vbox->add_child(base_tiles_root_control);
701
702
background_left = memnew(Control);
703
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
704
background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
705
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
706
background_left->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_left));
707
base_tiles_root_control->add_child(background_left);
708
709
base_tiles_drawing_root = memnew(Control);
710
base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
711
base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
712
base_tiles_root_control->add_child(base_tiles_drawing_root);
713
714
base_tiles_draw = memnew(Control);
715
base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
716
base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
717
base_tiles_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles));
718
base_tiles_drawing_root->add_child(base_tiles_draw);
719
720
base_tiles_texture_grid = memnew(Control);
721
base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
722
base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
723
base_tiles_texture_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
724
base_tiles_drawing_root->add_child(base_tiles_texture_grid);
725
726
base_tiles_shape_grid = memnew(Control);
727
base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
728
base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
729
base_tiles_shape_grid->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
730
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
731
732
// Alternative tiles.
733
Label *alternative_tiles_label = memnew(Label);
734
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
735
alternative_tiles_label->set_text(TTR("Alternative Tiles"));
736
alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
737
right_vbox->add_child(alternative_tiles_label);
738
739
alternative_tiles_root_control = memnew(Control);
740
alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
741
alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
742
alternative_tiles_root_control->connect(SceneStringName(gui_input), callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
743
right_vbox->add_child(alternative_tiles_root_control);
744
745
background_right = memnew(Control);
746
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
747
background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
748
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
749
background_right->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_background_right));
750
alternative_tiles_root_control->add_child(background_right);
751
752
alternative_tiles_drawing_root = memnew(Control);
753
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
754
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
755
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
756
757
alternatives_draw = memnew(Control);
758
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
759
alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
760
alternatives_draw->connect(SceneStringName(draw), callable_mp(this, &TileAtlasView::_draw_alternatives));
761
alternative_tiles_drawing_root->add_child(alternatives_draw);
762
}
763
764
TileAtlasView::~TileAtlasView() {
765
_clear_material_canvas_items();
766
}
767
768