Add per-tile flipping and transposing
This commit is contained in:
parent
fc99492d30
commit
a9f3154da3
@ -287,5 +287,20 @@
|
|||||||
<constant name="TILE_ANIMATION_MODE_MAX" value="2" enum="TileAnimationMode">
|
<constant name="TILE_ANIMATION_MODE_MAX" value="2" enum="TileAnimationMode">
|
||||||
Represents the size of the [enum TileAnimationMode] enum.
|
Represents the size of the [enum TileAnimationMode] enum.
|
||||||
</constant>
|
</constant>
|
||||||
|
<constant name="TRANSFORM_FLIP_H" value="4096">
|
||||||
|
Represents cell's horizontal flip flag. Should be used directly with [TileMap] to flip placed tiles by altering their alternative IDs.
|
||||||
|
[codeblock]
|
||||||
|
var alternate_id = $TileMap.get_cell_alternative_tile(0, Vector2i(2, 2))
|
||||||
|
if not alternate_id & TileSetAtlasSource.TRANSFORM_FLIP_H:
|
||||||
|
# If tile is not already flipped, flip it.
|
||||||
|
$TileMap.set_cell(0, Vector2i(2, 2), source_id, atlas_coords, alternate_id | TileSetAtlasSource.TRANSFORM_FLIP_H)
|
||||||
|
[/codeblock]
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_FLIP_V" value="8192">
|
||||||
|
Represents cell's vertical flip flag. See [constant TRANSFORM_FLIP_H] for usage.
|
||||||
|
</constant>
|
||||||
|
<constant name="TRANSFORM_TRANSPOSE" value="16384">
|
||||||
|
Represents cell's transposed flag. See [constant TRANSFORM_FLIP_H] for usage.
|
||||||
|
</constant>
|
||||||
</constants>
|
</constants>
|
||||||
</class>
|
</class>
|
||||||
|
@ -73,34 +73,21 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
|
|||||||
|
|
||||||
// Show only the correct settings.
|
// Show only the correct settings.
|
||||||
if (tool_buttons_group->get_pressed_button() == select_tool_button) {
|
if (tool_buttons_group->get_pressed_button() == select_tool_button) {
|
||||||
} else if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
|
transform_toolbar->show();
|
||||||
|
} else if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
|
||||||
tools_settings_vsep->show();
|
tools_settings_vsep->show();
|
||||||
picker_button->show();
|
picker_button->show();
|
||||||
erase_button->show();
|
erase_button->show();
|
||||||
|
transform_toolbar->show();
|
||||||
tools_settings_vsep_2->show();
|
tools_settings_vsep_2->show();
|
||||||
random_tile_toggle->show();
|
random_tile_toggle->show();
|
||||||
scatter_label->show();
|
scatter_label->show();
|
||||||
scatter_spinbox->show();
|
scatter_spinbox->show();
|
||||||
} else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
|
} else {
|
||||||
tools_settings_vsep->show();
|
|
||||||
picker_button->show();
|
|
||||||
erase_button->show();
|
|
||||||
tools_settings_vsep_2->show();
|
|
||||||
random_tile_toggle->show();
|
|
||||||
scatter_label->show();
|
|
||||||
scatter_spinbox->show();
|
|
||||||
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
|
|
||||||
tools_settings_vsep->show();
|
|
||||||
picker_button->show();
|
|
||||||
erase_button->show();
|
|
||||||
tools_settings_vsep_2->show();
|
|
||||||
random_tile_toggle->show();
|
|
||||||
scatter_label->show();
|
|
||||||
scatter_spinbox->show();
|
|
||||||
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
|
||||||
tools_settings_vsep->show();
|
tools_settings_vsep->show();
|
||||||
picker_button->show();
|
picker_button->show();
|
||||||
erase_button->show();
|
erase_button->show();
|
||||||
|
transform_toolbar->show();
|
||||||
tools_settings_vsep_2->show();
|
tools_settings_vsep_2->show();
|
||||||
bucket_contiguous_checkbox->show();
|
bucket_contiguous_checkbox->show();
|
||||||
random_tile_toggle->show();
|
random_tile_toggle->show();
|
||||||
@ -109,6 +96,31 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileMapEditorTilesPlugin::_update_transform_buttons() {
|
||||||
|
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
||||||
|
if (!tile_map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<TileSet> tile_set = tile_map->get_tileset();
|
||||||
|
if (tile_set.is_null() || selection_pattern.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE || selection_pattern->get_size() == Vector2i(1, 1)) {
|
||||||
|
transform_button_rotate_left->set_disabled(false);
|
||||||
|
transform_button_rotate_left->set_tooltip_text("");
|
||||||
|
transform_button_rotate_right->set_disabled(false);
|
||||||
|
transform_button_rotate_right->set_tooltip_text("");
|
||||||
|
} else {
|
||||||
|
const String tooltip_text = TTR("Can't rotate patterns when using non-square tile grid.");
|
||||||
|
transform_button_rotate_left->set_disabled(true);
|
||||||
|
transform_button_rotate_left->set_tooltip_text(tooltip_text);
|
||||||
|
transform_button_rotate_right->set_disabled(true);
|
||||||
|
transform_button_rotate_right->set_tooltip_text(tooltip_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
|
Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
|
||||||
Vector<TileMapSubEditorPlugin::TabData> tabs;
|
Vector<TileMapSubEditorPlugin::TabData> tabs;
|
||||||
tabs.push_back({ toolbar, tiles_bottom_panel });
|
tabs.push_back({ toolbar, tiles_bottom_panel });
|
||||||
@ -480,6 +492,11 @@ void TileMapEditorTilesPlugin::_update_theme() {
|
|||||||
erase_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser")));
|
erase_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser")));
|
||||||
random_tile_toggle->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator")));
|
random_tile_toggle->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator")));
|
||||||
|
|
||||||
|
transform_button_rotate_left->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateLeft"));
|
||||||
|
transform_button_rotate_right->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateRight"));
|
||||||
|
transform_button_flip_h->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorX"));
|
||||||
|
transform_button_flip_v->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorY"));
|
||||||
|
|
||||||
missing_atlas_texture_icon = tiles_bottom_panel->get_editor_theme_icon(SNAME("TileSet"));
|
missing_atlas_texture_icon = tiles_bottom_panel->get_editor_theme_icon(SNAME("TileSet"));
|
||||||
_update_tile_set_sources_list();
|
_update_tile_set_sources_list();
|
||||||
}
|
}
|
||||||
@ -573,8 +590,17 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
|
|||||||
Ref<InputEventKey> k = p_event;
|
Ref<InputEventKey> k = p_event;
|
||||||
if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
|
if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
|
||||||
for (BaseButton *b : viewport_shortcut_buttons) {
|
for (BaseButton *b : viewport_shortcut_buttons) {
|
||||||
|
if (b->is_disabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
|
if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
|
||||||
|
if (b->is_toggle_mode()) {
|
||||||
b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
|
b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
|
||||||
|
} else {
|
||||||
|
// Can't press a button without toggle mode, so just emit the signal directly.
|
||||||
|
b->emit_signal(SNAME("pressed"));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -924,18 +950,18 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|||||||
Rect2 dest_rect;
|
Rect2 dest_rect;
|
||||||
dest_rect.size = source_rect.size;
|
dest_rect.size = source_rect.size;
|
||||||
|
|
||||||
bool transpose = tile_data->get_transpose();
|
bool transpose = tile_data->get_transpose() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
||||||
if (transpose) {
|
if (transpose) {
|
||||||
dest_rect.position = (tile_map->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
|
dest_rect.position = (tile_map->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
|
||||||
} else {
|
} else {
|
||||||
dest_rect.position = (tile_map->map_to_local(E.key) - dest_rect.size / 2 - tile_offset);
|
dest_rect.position = (tile_map->map_to_local(E.key) - dest_rect.size / 2 - tile_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile_data->get_flip_h()) {
|
if (tile_data->get_flip_h() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
|
||||||
dest_rect.size.x = -dest_rect.size.x;
|
dest_rect.size.x = -dest_rect.size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile_data->get_flip_v()) {
|
if (tile_data->get_flip_v() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
|
||||||
dest_rect.size.y = -dest_rect.size.y;
|
dest_rect.size.y = -dest_rect.size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1475,6 +1501,94 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
|
|||||||
drag_type = DRAG_TYPE_NONE;
|
drag_type = DRAG_TYPE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileMapEditorTilesPlugin::_apply_transform(int p_type) {
|
||||||
|
if (selection_pattern.is_null() || selection_pattern->is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<TileMapPattern> transformed_pattern;
|
||||||
|
transformed_pattern.instantiate();
|
||||||
|
bool keep_shape = selection_pattern->get_size() == Vector2i(1, 1);
|
||||||
|
|
||||||
|
Vector2i size = selection_pattern->get_size();
|
||||||
|
for (int y = 0; y < size.y; y++) {
|
||||||
|
for (int x = 0; x < size.x; x++) {
|
||||||
|
Vector2i src_coords = Vector2i(x, y);
|
||||||
|
if (!selection_pattern->has_cell(src_coords)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2i dst_coords;
|
||||||
|
|
||||||
|
if (keep_shape) {
|
||||||
|
dst_coords = src_coords;
|
||||||
|
} else if (p_type == TRANSFORM_ROTATE_LEFT) {
|
||||||
|
dst_coords = Vector2i(y, size.x - x - 1);
|
||||||
|
} else if (p_type == TRANSFORM_ROTATE_RIGHT) {
|
||||||
|
dst_coords = Vector2i(size.y - y - 1, x);
|
||||||
|
} else if (p_type == TRANSFORM_FLIP_H) {
|
||||||
|
dst_coords = Vector2i(size.x - x - 1, y);
|
||||||
|
} else if (p_type == TRANSFORM_FLIP_V) {
|
||||||
|
dst_coords = Vector2i(x, size.y - y - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
transformed_pattern->set_cell(dst_coords,
|
||||||
|
selection_pattern->get_cell_source_id(src_coords), selection_pattern->get_cell_atlas_coords(src_coords),
|
||||||
|
_get_transformed_alternative(selection_pattern->get_cell_alternative_tile(src_coords), p_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selection_pattern = transformed_pattern;
|
||||||
|
CanvasItemEditor::get_singleton()->update_viewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TileMapEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, int p_transform) {
|
||||||
|
bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
||||||
|
bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
||||||
|
bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
||||||
|
|
||||||
|
switch (p_transform) {
|
||||||
|
case TRANSFORM_ROTATE_LEFT:
|
||||||
|
case TRANSFORM_ROTATE_RIGHT: {
|
||||||
|
// A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
|
||||||
|
const LocalVector<bool> rotation_matrix = {
|
||||||
|
0, 0, 0,
|
||||||
|
0, 1, 1,
|
||||||
|
1, 1, 0,
|
||||||
|
1, 0, 1,
|
||||||
|
1, 0, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
0, 1, 0,
|
||||||
|
1, 1, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (transform_flip_h == rotation_matrix[i * 3] && transform_flip_v == rotation_matrix[i * 3 + 1] && transform_transpose == rotation_matrix[i * 3 + 2]) {
|
||||||
|
if (p_transform == TRANSFORM_ROTATE_LEFT) {
|
||||||
|
i = i / 4 * 4 + (i + 1) % 4;
|
||||||
|
} else {
|
||||||
|
i = i / 4 * 4 + Math::posmod(i - 1, 4);
|
||||||
|
}
|
||||||
|
transform_flip_h = rotation_matrix[i * 3];
|
||||||
|
transform_flip_v = rotation_matrix[i * 3 + 1];
|
||||||
|
transform_transpose = rotation_matrix[i * 3 + 2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_FLIP_H: {
|
||||||
|
transform_flip_h = !transform_flip_h;
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_FLIP_V: {
|
||||||
|
transform_flip_v = !transform_flip_v;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TileSetAtlasSource::alternative_no_transform(p_alternative_id) |
|
||||||
|
int(transform_flip_h) * TileSetAtlasSource::TRANSFORM_FLIP_H |
|
||||||
|
int(transform_flip_v) * TileSetAtlasSource::TRANSFORM_FLIP_V |
|
||||||
|
int(transform_transpose) * TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
||||||
|
}
|
||||||
|
|
||||||
void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
|
void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
|
||||||
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
||||||
if (!tile_map) {
|
if (!tile_map) {
|
||||||
@ -1589,6 +1703,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
|
|||||||
coords_array.push_back(E);
|
coords_array.push_back(E);
|
||||||
}
|
}
|
||||||
selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
|
selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
|
||||||
|
_update_transform_buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
|
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
|
||||||
@ -1662,6 +1777,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_sele
|
|||||||
vertical_offset += MAX(organized_size.y, 1);
|
vertical_offset += MAX(organized_size.y, 1);
|
||||||
}
|
}
|
||||||
CanvasItemEditor::get_singleton()->update_viewport();
|
CanvasItemEditor::get_singleton()->update_viewport();
|
||||||
|
_update_transform_buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() {
|
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() {
|
||||||
@ -1700,6 +1816,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
|
|||||||
_update_source_display();
|
_update_source_display();
|
||||||
tile_atlas_control->queue_redraw();
|
tile_atlas_control->queue_redraw();
|
||||||
alternative_tiles_control->queue_redraw();
|
alternative_tiles_control->queue_redraw();
|
||||||
|
_update_transform_buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
|
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
|
||||||
@ -2161,6 +2278,39 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
|
|||||||
tools_settings->add_child(erase_button);
|
tools_settings->add_child(erase_button);
|
||||||
viewport_shortcut_buttons.push_back(erase_button);
|
viewport_shortcut_buttons.push_back(erase_button);
|
||||||
|
|
||||||
|
// Transform toolbar.
|
||||||
|
transform_toolbar = memnew(HBoxContainer);
|
||||||
|
tools_settings->add_child(transform_toolbar);
|
||||||
|
transform_toolbar->add_child(memnew(VSeparator));
|
||||||
|
|
||||||
|
transform_button_rotate_left = memnew(Button);
|
||||||
|
transform_button_rotate_left->set_flat(true);
|
||||||
|
transform_button_rotate_left->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_left", TTR("Rotate Tile Left"), Key::Z));
|
||||||
|
transform_toolbar->add_child(transform_button_rotate_left);
|
||||||
|
transform_button_rotate_left->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_LEFT));
|
||||||
|
viewport_shortcut_buttons.push_back(transform_button_rotate_left);
|
||||||
|
|
||||||
|
transform_button_rotate_right = memnew(Button);
|
||||||
|
transform_button_rotate_right->set_flat(true);
|
||||||
|
transform_button_rotate_right->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_right", TTR("Rotate Tile Right"), Key::X));
|
||||||
|
transform_toolbar->add_child(transform_button_rotate_right);
|
||||||
|
transform_button_rotate_right->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_RIGHT));
|
||||||
|
viewport_shortcut_buttons.push_back(transform_button_rotate_right);
|
||||||
|
|
||||||
|
transform_button_flip_h = memnew(Button);
|
||||||
|
transform_button_flip_h->set_flat(true);
|
||||||
|
transform_button_flip_h->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_horizontal", TTR("Flip Tile Horizontally"), Key::C));
|
||||||
|
transform_toolbar->add_child(transform_button_flip_h);
|
||||||
|
transform_button_flip_h->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_H));
|
||||||
|
viewport_shortcut_buttons.push_back(transform_button_flip_h);
|
||||||
|
|
||||||
|
transform_button_flip_v = memnew(Button);
|
||||||
|
transform_button_flip_v->set_flat(true);
|
||||||
|
transform_button_flip_v->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_vertical", TTR("Flip Tile Vertically"), Key::V));
|
||||||
|
transform_toolbar->add_child(transform_button_flip_v);
|
||||||
|
transform_button_flip_v->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_V));
|
||||||
|
viewport_shortcut_buttons.push_back(transform_button_flip_v);
|
||||||
|
|
||||||
// Separator 2.
|
// Separator 2.
|
||||||
tools_settings_vsep_2 = memnew(VSeparator);
|
tools_settings_vsep_2 = memnew(VSeparator);
|
||||||
tools_settings->add_child(tools_settings_vsep_2);
|
tools_settings->add_child(tools_settings_vsep_2);
|
||||||
@ -2352,25 +2502,11 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show only the correct settings.
|
// Show only the correct settings.
|
||||||
if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
|
if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
|
||||||
tools_settings_vsep->show();
|
tools_settings_vsep->show();
|
||||||
picker_button->show();
|
picker_button->show();
|
||||||
erase_button->show();
|
erase_button->show();
|
||||||
tools_settings_vsep_2->hide();
|
} else {
|
||||||
bucket_contiguous_checkbox->hide();
|
|
||||||
} else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
|
|
||||||
tools_settings_vsep->show();
|
|
||||||
picker_button->show();
|
|
||||||
erase_button->show();
|
|
||||||
tools_settings_vsep_2->hide();
|
|
||||||
bucket_contiguous_checkbox->hide();
|
|
||||||
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
|
|
||||||
tools_settings_vsep->show();
|
|
||||||
picker_button->show();
|
|
||||||
erase_button->show();
|
|
||||||
tools_settings_vsep_2->hide();
|
|
||||||
bucket_contiguous_checkbox->hide();
|
|
||||||
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
|
||||||
tools_settings_vsep->show();
|
tools_settings_vsep->show();
|
||||||
picker_button->show();
|
picker_button->show();
|
||||||
erase_button->show();
|
erase_button->show();
|
||||||
@ -3496,7 +3632,6 @@ TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
|
|||||||
|
|
||||||
void TileMapEditor::_notification(int p_what) {
|
void TileMapEditor::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ENTER_TREE:
|
|
||||||
case NOTIFICATION_THEME_CHANGED: {
|
case NOTIFICATION_THEME_CHANGED: {
|
||||||
missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
|
missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
|
||||||
warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
|
warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
|
||||||
|
@ -48,6 +48,8 @@
|
|||||||
#include "scene/gui/tab_bar.h"
|
#include "scene/gui/tab_bar.h"
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
|
|
||||||
|
class TileMapEditor;
|
||||||
|
|
||||||
class TileMapSubEditorPlugin : public Object {
|
class TileMapSubEditorPlugin : public Object {
|
||||||
public:
|
public:
|
||||||
struct TabData {
|
struct TabData {
|
||||||
@ -68,6 +70,14 @@ public:
|
|||||||
class TileMapEditorTilesPlugin : public TileMapSubEditorPlugin {
|
class TileMapEditorTilesPlugin : public TileMapSubEditorPlugin {
|
||||||
GDCLASS(TileMapEditorTilesPlugin, TileMapSubEditorPlugin);
|
GDCLASS(TileMapEditorTilesPlugin, TileMapSubEditorPlugin);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
TRANSFORM_ROTATE_LEFT,
|
||||||
|
TRANSFORM_ROTATE_RIGHT,
|
||||||
|
TRANSFORM_FLIP_H,
|
||||||
|
TRANSFORM_FLIP_V,
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ObjectID tile_map_id;
|
ObjectID tile_map_id;
|
||||||
int tile_map_layer = -1;
|
int tile_map_layer = -1;
|
||||||
@ -89,6 +99,12 @@ private:
|
|||||||
Button *picker_button = nullptr;
|
Button *picker_button = nullptr;
|
||||||
Button *erase_button = nullptr;
|
Button *erase_button = nullptr;
|
||||||
|
|
||||||
|
HBoxContainer *transform_toolbar = nullptr;
|
||||||
|
Button *transform_button_rotate_left = nullptr;
|
||||||
|
Button *transform_button_rotate_right = nullptr;
|
||||||
|
Button *transform_button_flip_h = nullptr;
|
||||||
|
Button *transform_button_flip_v = nullptr;
|
||||||
|
|
||||||
VSeparator *tools_settings_vsep_2 = nullptr;
|
VSeparator *tools_settings_vsep_2 = nullptr;
|
||||||
CheckBox *bucket_contiguous_checkbox = nullptr;
|
CheckBox *bucket_contiguous_checkbox = nullptr;
|
||||||
Button *random_tile_toggle = nullptr;
|
Button *random_tile_toggle = nullptr;
|
||||||
@ -101,6 +117,7 @@ private:
|
|||||||
void _on_scattering_spinbox_changed(double p_value);
|
void _on_scattering_spinbox_changed(double p_value);
|
||||||
|
|
||||||
void _update_toolbar();
|
void _update_toolbar();
|
||||||
|
void _update_transform_buttons();
|
||||||
|
|
||||||
///// Tilemap editing. /////
|
///// Tilemap editing. /////
|
||||||
bool has_mouse = false;
|
bool has_mouse = false;
|
||||||
@ -129,6 +146,9 @@ private:
|
|||||||
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
||||||
void _stop_dragging();
|
void _stop_dragging();
|
||||||
|
|
||||||
|
void _apply_transform(int p_type);
|
||||||
|
int _get_transformed_alternative(int p_alternative_id, int p_transform);
|
||||||
|
|
||||||
///// Selection system. /////
|
///// Selection system. /////
|
||||||
RBSet<Vector2i> tile_map_selection;
|
RBSet<Vector2i> tile_map_selection;
|
||||||
Ref<TileMapPattern> tile_map_clipboard;
|
Ref<TileMapPattern> tile_map_clipboard;
|
||||||
|
@ -548,7 +548,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
|
|||||||
RID occluder_id = rs->canvas_light_occluder_create();
|
RID occluder_id = rs->canvas_light_occluder_create();
|
||||||
rs->canvas_light_occluder_set_enabled(occluder_id, node_visible);
|
rs->canvas_light_occluder_set_enabled(occluder_id, node_visible);
|
||||||
rs->canvas_light_occluder_set_transform(occluder_id, tile_map_node->get_global_transform() * xform);
|
rs->canvas_light_occluder_set_transform(occluder_id, tile_map_node->get_global_transform() * xform);
|
||||||
rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid());
|
rs->canvas_light_occluder_set_polygon(occluder_id, tile_map_node->get_transformed_polygon(Ref<Resource>(tile_data->get_occluder(i)), r_cell_data.cell.alternative_tile)->get_rid());
|
||||||
rs->canvas_light_occluder_attach_to_canvas(occluder_id, tile_map_node->get_canvas());
|
rs->canvas_light_occluder_attach_to_canvas(occluder_id, tile_map_node->get_canvas());
|
||||||
rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
|
rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
|
||||||
r_cell_data.occluders.push_back(occluder_id);
|
r_cell_data.occluders.push_back(occluder_id);
|
||||||
@ -783,6 +783,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
|
|||||||
for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
|
for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
|
||||||
// Add decomposed convex shapes.
|
// Add decomposed convex shapes.
|
||||||
Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index);
|
Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index);
|
||||||
|
shape = tile_map_node->get_transformed_polygon(Ref<Resource>(shape), c.alternative_tile);
|
||||||
ps->body_add_shape(body, shape->get_rid());
|
ps->body_add_shape(body, shape->get_rid());
|
||||||
ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin);
|
ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin);
|
||||||
|
|
||||||
@ -985,6 +986,7 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
|
|||||||
for (unsigned int navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) {
|
for (unsigned int navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) {
|
||||||
Ref<NavigationPolygon> navigation_polygon;
|
Ref<NavigationPolygon> navigation_polygon;
|
||||||
navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index);
|
navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index);
|
||||||
|
navigation_polygon = tile_map_node->get_transformed_polygon(Ref<Resource>(navigation_polygon), c.alternative_tile);
|
||||||
|
|
||||||
RID ®ion = r_cell_data.navigation_regions[navigation_layer_index];
|
RID ®ion = r_cell_data.navigation_regions[navigation_layer_index];
|
||||||
|
|
||||||
@ -1074,6 +1076,7 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
|
|||||||
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
|
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
|
||||||
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index);
|
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index);
|
||||||
if (navigation_polygon.is_valid()) {
|
if (navigation_polygon.is_valid()) {
|
||||||
|
navigation_polygon = tile_map_node->get_transformed_polygon(Ref<Resource>(navigation_polygon), c.alternative_tile);
|
||||||
Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
|
Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
|
||||||
if (navigation_polygon_vertices.size() < 3) {
|
if (navigation_polygon_vertices.size() < 3) {
|
||||||
continue;
|
continue;
|
||||||
@ -3012,6 +3015,7 @@ void TileMap::_internal_update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update dirty quadrants on layers.
|
// Update dirty quadrants on layers.
|
||||||
|
polygon_cache.clear();
|
||||||
for (Ref<TileMapLayer> &layer : layers) {
|
for (Ref<TileMapLayer> &layer : layers) {
|
||||||
layer->internal_update();
|
layer->internal_update();
|
||||||
}
|
}
|
||||||
@ -3100,18 +3104,18 @@ void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<
|
|||||||
dest_rect.size.x += FP_ADJUST;
|
dest_rect.size.x += FP_ADJUST;
|
||||||
dest_rect.size.y += FP_ADJUST;
|
dest_rect.size.y += FP_ADJUST;
|
||||||
|
|
||||||
bool transpose = tile_data->get_transpose();
|
bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
||||||
if (transpose) {
|
if (transpose) {
|
||||||
dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
|
dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
|
||||||
} else {
|
} else {
|
||||||
dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
|
dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile_data->get_flip_h()) {
|
if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
|
||||||
dest_rect.size.x = -dest_rect.size.x;
|
dest_rect.size.x = -dest_rect.size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile_data->get_flip_v()) {
|
if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
|
||||||
dest_rect.size.y = -dest_rect.size.y;
|
dest_rect.size.y = -dest_rect.size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3482,6 +3486,37 @@ Rect2 TileMap::_edit_get_rect() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PackedVector2Array TileMap::_get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id) {
|
||||||
|
const Vector2 *r = p_vertices.ptr();
|
||||||
|
int size = p_vertices.size();
|
||||||
|
|
||||||
|
PackedVector2Array new_points;
|
||||||
|
new_points.resize(size);
|
||||||
|
Vector2 *w = new_points.ptrw();
|
||||||
|
|
||||||
|
bool flip_h = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
|
||||||
|
bool flip_v = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
|
||||||
|
bool transpose = (p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
Vector2 v;
|
||||||
|
if (transpose) {
|
||||||
|
v = Vector2(r[i].y, r[i].x);
|
||||||
|
} else {
|
||||||
|
v = r[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flip_h) {
|
||||||
|
v.x *= -1;
|
||||||
|
}
|
||||||
|
if (flip_v) {
|
||||||
|
v.y *= -1;
|
||||||
|
}
|
||||||
|
w[i] = v;
|
||||||
|
}
|
||||||
|
return new_points;
|
||||||
|
}
|
||||||
|
|
||||||
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
|
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
Vector<String> components = String(p_name).split("/", true, 2);
|
Vector<String> components = String(p_name).split("/", true, 2);
|
||||||
if (p_name == "format") {
|
if (p_name == "format") {
|
||||||
@ -4384,6 +4419,57 @@ void TileMap::draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_ce
|
|||||||
#undef DRAW_SIDE_IF_NEEDED
|
#undef DRAW_SIDE_IF_NEEDED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<Resource> TileMap::get_transformed_polygon(Ref<Resource> p_polygon, int p_alternative_id) {
|
||||||
|
if (!bool(p_alternative_id & (TileSetAtlasSource::TRANSFORM_FLIP_H | TileSetAtlasSource::TRANSFORM_FLIP_V | TileSetAtlasSource::TRANSFORM_TRANSPOSE))) {
|
||||||
|
return p_polygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
HashMap<Pair<Ref<Resource>, int>, Ref<Resource>, PairHash<Ref<Resource>, int>>::Iterator E = polygon_cache.find(Pair<Ref<Resource>, int>(p_polygon, p_alternative_id));
|
||||||
|
if (E) {
|
||||||
|
return E->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ConvexPolygonShape2D> col = p_polygon;
|
||||||
|
if (col.is_valid()) {
|
||||||
|
Ref<ConvexPolygonShape2D> ret;
|
||||||
|
ret.instantiate();
|
||||||
|
ret->set_points(_get_transformed_vertices(col->get_points(), p_alternative_id));
|
||||||
|
polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<NavigationPolygon> nav = p_polygon;
|
||||||
|
if (nav.is_valid()) {
|
||||||
|
PackedVector2Array new_points = _get_transformed_vertices(nav->get_vertices(), p_alternative_id);
|
||||||
|
Ref<NavigationPolygon> ret;
|
||||||
|
ret.instantiate();
|
||||||
|
ret->set_vertices(new_points);
|
||||||
|
|
||||||
|
PackedInt32Array indices;
|
||||||
|
indices.resize(new_points.size());
|
||||||
|
int *w = indices.ptrw();
|
||||||
|
for (int i = 0; i < new_points.size(); i++) {
|
||||||
|
w[i] = i;
|
||||||
|
}
|
||||||
|
ret->add_polygon(indices);
|
||||||
|
polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<OccluderPolygon2D> ocd = p_polygon;
|
||||||
|
if (ocd.is_valid()) {
|
||||||
|
Ref<OccluderPolygon2D> ret;
|
||||||
|
ret.instantiate();
|
||||||
|
ret->set_polygon(_get_transformed_vertices(ocd->get_polygon(), p_alternative_id));
|
||||||
|
polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_polygon;
|
||||||
|
}
|
||||||
|
|
||||||
PackedStringArray TileMap::get_configuration_warnings() const {
|
PackedStringArray TileMap::get_configuration_warnings() const {
|
||||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||||
|
|
||||||
|
@ -455,6 +455,10 @@ private:
|
|||||||
|
|
||||||
void _tile_set_changed();
|
void _tile_set_changed();
|
||||||
|
|
||||||
|
// Polygons.
|
||||||
|
HashMap<Pair<Ref<Resource>, int>, Ref<Resource>, PairHash<Ref<Resource>, int>> polygon_cache;
|
||||||
|
PackedVector2Array _get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||||
@ -595,6 +599,7 @@ public:
|
|||||||
// Helpers?
|
// Helpers?
|
||||||
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &coords);
|
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &coords);
|
||||||
void draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D());
|
void draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D());
|
||||||
|
Ref<Resource> get_transformed_polygon(Ref<Resource> p_polygon, int p_alternative_id);
|
||||||
|
|
||||||
// Virtual function to modify the TileData at runtime.
|
// Virtual function to modify the TileData at runtime.
|
||||||
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
|
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
|
||||||
|
@ -4464,6 +4464,10 @@ bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atl
|
|||||||
return rect.has_point(p_position);
|
return rect.has_point(p_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) {
|
||||||
|
return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE);
|
||||||
|
}
|
||||||
|
|
||||||
// Getters for texture and tile region (padded or not)
|
// Getters for texture and tile region (padded or not)
|
||||||
Ref<Texture2D> TileSetAtlasSource::get_runtime_texture() const {
|
Ref<Texture2D> TileSetAtlasSource::get_runtime_texture() const {
|
||||||
if (use_texture_padding) {
|
if (use_texture_padding) {
|
||||||
@ -4547,6 +4551,7 @@ int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, i
|
|||||||
void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) {
|
void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) {
|
||||||
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
||||||
ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
||||||
|
p_alternative_tile = alternative_no_transform(p_alternative_tile);
|
||||||
ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed.");
|
ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed.");
|
||||||
|
|
||||||
memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]);
|
memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]);
|
||||||
@ -4560,6 +4565,7 @@ void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords,
|
|||||||
void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) {
|
void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) {
|
||||||
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
||||||
ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
||||||
|
p_alternative_tile = alternative_no_transform(p_alternative_tile);
|
||||||
ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified.");
|
ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified.");
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords)));
|
ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords)));
|
||||||
@ -4576,7 +4582,7 @@ void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords,
|
|||||||
|
|
||||||
bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
|
bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
|
||||||
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
||||||
return tiles[p_atlas_coords].alternatives.has(p_alternative_tile);
|
return tiles[p_atlas_coords].alternatives.has(alternative_no_transform(p_alternative_tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const {
|
int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const {
|
||||||
@ -4591,6 +4597,7 @@ int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coord
|
|||||||
|
|
||||||
int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
|
int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
|
||||||
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
||||||
|
p_index = alternative_no_transform(p_index);
|
||||||
ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
|
ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
|
||||||
|
|
||||||
return tiles[p_atlas_coords].alternatives_ids[p_index];
|
return tiles[p_atlas_coords].alternatives_ids[p_index];
|
||||||
@ -4598,6 +4605,7 @@ int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, i
|
|||||||
|
|
||||||
TileData *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
|
TileData *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
|
||||||
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
|
||||||
|
p_alternative_tile = alternative_no_transform(p_alternative_tile);
|
||||||
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
||||||
|
|
||||||
return tiles[p_atlas_coords].alternatives[p_alternative_tile];
|
return tiles[p_atlas_coords].alternatives[p_alternative_tile];
|
||||||
@ -4668,6 +4676,10 @@ void TileSetAtlasSource::_bind_methods() {
|
|||||||
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_DEFAULT)
|
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_DEFAULT)
|
||||||
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_RANDOM_START_TIMES)
|
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_RANDOM_START_TIMES)
|
||||||
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_MAX)
|
BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_MAX)
|
||||||
|
|
||||||
|
BIND_CONSTANT(TRANSFORM_FLIP_H)
|
||||||
|
BIND_CONSTANT(TRANSFORM_FLIP_V)
|
||||||
|
BIND_CONSTANT(TRANSFORM_TRANSPOSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSetAtlasSource::~TileSetAtlasSource() {
|
TileSetAtlasSource::~TileSetAtlasSource() {
|
||||||
@ -4681,6 +4693,7 @@ TileSetAtlasSource::~TileSetAtlasSource() {
|
|||||||
|
|
||||||
TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) {
|
TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) {
|
||||||
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
|
||||||
|
p_alternative_tile = alternative_no_transform(p_alternative_tile);
|
||||||
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
|
||||||
|
|
||||||
return tiles[p_atlas_coords].alternatives[p_alternative_tile];
|
return tiles[p_atlas_coords].alternatives[p_alternative_tile];
|
||||||
|
@ -62,10 +62,10 @@ class TileSetPluginAtlasNavigation;
|
|||||||
|
|
||||||
union TileMapCell {
|
union TileMapCell {
|
||||||
struct {
|
struct {
|
||||||
int32_t source_id : 16;
|
int16_t source_id;
|
||||||
int16_t coord_x : 16;
|
int16_t coord_x;
|
||||||
int16_t coord_y : 16;
|
int16_t coord_y;
|
||||||
int32_t alternative_tile : 16;
|
int16_t alternative_tile;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t _u64t;
|
uint64_t _u64t;
|
||||||
@ -598,6 +598,12 @@ public:
|
|||||||
TILE_ANIMATION_MODE_MAX,
|
TILE_ANIMATION_MODE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TransformBits {
|
||||||
|
TRANSFORM_FLIP_H = 1 << 12,
|
||||||
|
TRANSFORM_FLIP_V = 1 << 13,
|
||||||
|
TRANSFORM_TRANSPOSE = 1 << 14,
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct TileAlternativesData {
|
struct TileAlternativesData {
|
||||||
Vector2i size_in_atlas = Vector2i(1, 1);
|
Vector2i size_in_atlas = Vector2i(1, 1);
|
||||||
@ -736,6 +742,8 @@ public:
|
|||||||
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
||||||
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
|
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
|
||||||
|
|
||||||
|
static int alternative_no_transform(int p_alternative_id);
|
||||||
|
|
||||||
// Getters for texture and tile region (padded or not)
|
// Getters for texture and tile region (padded or not)
|
||||||
Ref<Texture2D> get_runtime_texture() const;
|
Ref<Texture2D> get_runtime_texture() const;
|
||||||
Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
|
||||||
|
Loading…
Reference in New Issue
Block a user