mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 05:31:01 +00:00 
			
		
		
		
	Add per-tile flipping and transposing
This commit is contained in:
		
							parent
							
								
									fc99492d30
								
							
						
					
					
						commit
						a9f3154da3
					
				
					 7 changed files with 330 additions and 48 deletions
				
			
		|  | @ -73,34 +73,21 @@ void TileMapEditorTilesPlugin::_update_toolbar() { | |||
| 
 | ||||
| 	// Show only the correct settings.
 | ||||
| 	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(); | ||||
| 		picker_button->show(); | ||||
| 		erase_button->show(); | ||||
| 		transform_toolbar->show(); | ||||
| 		tools_settings_vsep_2->show(); | ||||
| 		random_tile_toggle->show(); | ||||
| 		scatter_label->show(); | ||||
| 		scatter_spinbox->show(); | ||||
| 	} 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->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) { | ||||
| 	} else { | ||||
| 		tools_settings_vsep->show(); | ||||
| 		picker_button->show(); | ||||
| 		erase_button->show(); | ||||
| 		transform_toolbar->show(); | ||||
| 		tools_settings_vsep_2->show(); | ||||
| 		bucket_contiguous_checkbox->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> tabs; | ||||
| 	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"))); | ||||
| 	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")); | ||||
| 	_update_tile_set_sources_list(); | ||||
| } | ||||
|  | @ -573,8 +590,17 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p | |||
| 	Ref<InputEventKey> k = p_event; | ||||
| 	if (k.is_valid() && k->is_pressed() && !k->is_echo()) { | ||||
| 		for (BaseButton *b : viewport_shortcut_buttons) { | ||||
| 			if (b->is_disabled()) { | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) { | ||||
| 				b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed()); | ||||
| 				if (b->is_toggle_mode()) { | ||||
| 					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; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -924,18 +950,18 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over | |||
| 							Rect2 dest_rect; | ||||
| 							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) { | ||||
| 								dest_rect.position = (tile_map->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); | ||||
| 							} else { | ||||
| 								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; | ||||
| 							} | ||||
| 
 | ||||
| 							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; | ||||
| 							} | ||||
| 
 | ||||
|  | @ -1475,6 +1501,94 @@ void TileMapEditorTilesPlugin::_stop_dragging() { | |||
| 	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() { | ||||
| 	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); | ||||
| 	if (!tile_map) { | ||||
|  | @ -1589,6 +1703,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection( | |||
| 		coords_array.push_back(E); | ||||
| 	} | ||||
| 	selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); | ||||
| 	_update_transform_buttons(); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 	} | ||||
| 	CanvasItemEditor::get_singleton()->update_viewport(); | ||||
| 	_update_transform_buttons(); | ||||
| } | ||||
| 
 | ||||
| void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() { | ||||
|  | @ -1700,6 +1816,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern( | |||
| 	_update_source_display(); | ||||
| 	tile_atlas_control->queue_redraw(); | ||||
| 	alternative_tiles_control->queue_redraw(); | ||||
| 	_update_transform_buttons(); | ||||
| } | ||||
| 
 | ||||
| void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { | ||||
|  | @ -2161,6 +2278,39 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { | |||
| 	tools_settings->add_child(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.
 | ||||
| 	tools_settings_vsep_2 = memnew(VSeparator); | ||||
| 	tools_settings->add_child(tools_settings_vsep_2); | ||||
|  | @ -2352,25 +2502,11 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() { | |||
| 	} | ||||
| 
 | ||||
| 	// 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(); | ||||
| 		picker_button->show(); | ||||
| 		erase_button->show(); | ||||
| 		tools_settings_vsep_2->hide(); | ||||
| 		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) { | ||||
| 	} else { | ||||
| 		tools_settings_vsep->show(); | ||||
| 		picker_button->show(); | ||||
| 		erase_button->show(); | ||||
|  | @ -3496,7 +3632,6 @@ TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() { | |||
| 
 | ||||
| void TileMapEditor::_notification(int p_what) { | ||||
| 	switch (p_what) { | ||||
| 		case NOTIFICATION_ENTER_TREE: | ||||
| 		case NOTIFICATION_THEME_CHANGED: { | ||||
| 			missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning")); | ||||
| 			warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern")); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 kobewi
						kobewi