Explorar el Código

Merge pull request #58375 from KoBeWi/temporary_pivot_is_temporary

Add temporary pivot for rotating multiple 2D nodes
Rémi Verschelde hace 1 año
padre
commit
610a9bed13

+ 66 - 15
editor/plugins/canvas_item_editor_plugin.cpp

@@ -1344,22 +1344,33 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
 
 
 	// Drag the pivot (in pivot mode / with V key)
 	// Drag the pivot (in pivot mode / with V key)
 	if (drag_type == DRAG_NONE) {
 	if (drag_type == DRAG_NONE) {
+		bool move_temp_pivot = ((b.is_valid() && b->is_shift_pressed()) || (k.is_valid() && k->is_shift_pressed()));
+
 		if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
 		if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
-				(k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask().is_empty())) {
+				(k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && (k->get_modifiers_mask().is_empty() || move_temp_pivot))) {
 			List<CanvasItem *> selection = _get_edited_canvas_items();
 			List<CanvasItem *> selection = _get_edited_canvas_items();
 
 
 			// Filters the selection with nodes that allow setting the pivot
 			// Filters the selection with nodes that allow setting the pivot
 			drag_selection = List<CanvasItem *>();
 			drag_selection = List<CanvasItem *>();
 			for (CanvasItem *ci : selection) {
 			for (CanvasItem *ci : selection) {
-				if (ci->_edit_use_pivot()) {
+				if (ci->_edit_use_pivot() || move_temp_pivot) {
 					drag_selection.push_back(ci);
 					drag_selection.push_back(ci);
 				}
 				}
 			}
 			}
 
 
 			// Start dragging if we still have nodes
 			// Start dragging if we still have nodes
 			if (drag_selection.size() > 0) {
 			if (drag_selection.size() > 0) {
+				Vector2 event_pos = (b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position();
+
+				if (move_temp_pivot) {
+					drag_type = DRAG_TEMP_PIVOT;
+					temp_pivot = transform.affine_inverse().xform(event_pos);
+					viewport->queue_redraw();
+					return true;
+				}
+
 				_save_canvas_item_state(drag_selection);
 				_save_canvas_item_state(drag_selection);
-				drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position());
+				drag_from = transform.affine_inverse().xform(event_pos);
 				Vector2 new_pos;
 				Vector2 new_pos;
 				if (drag_selection.size() == 1) {
 				if (drag_selection.size() == 1) {
 					new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]);
 					new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]);
@@ -1416,6 +1427,20 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
 			return true;
 			return true;
 		}
 		}
 	}
 	}
+
+	if (drag_type == DRAG_TEMP_PIVOT) {
+		if (m.is_valid()) {
+			temp_pivot = transform.affine_inverse().xform(m->get_position());
+			viewport->queue_redraw();
+			return true;
+		}
+
+		if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
+				(k.is_valid() && !k->is_pressed() && k->get_keycode() == Key::V)) {
+			drag_type = DRAG_NONE;
+			return true;
+		}
+	}
 	return false;
 	return false;
 }
 }
 
 
@@ -1441,7 +1466,9 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
 					drag_type = DRAG_ROTATE;
 					drag_type = DRAG_ROTATE;
 					drag_from = transform.affine_inverse().xform(b->get_position());
 					drag_from = transform.affine_inverse().xform(b->get_position());
 					CanvasItem *ci = drag_selection[0];
 					CanvasItem *ci = drag_selection[0];
-					if (ci->_edit_use_pivot()) {
+					if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
+						drag_rotation_center = temp_pivot;
+					} else if (ci->_edit_use_pivot()) {
 						drag_rotation_center = ci->get_global_transform_with_canvas().xform(ci->_edit_get_pivot());
 						drag_rotation_center = ci->get_global_transform_with_canvas().xform(ci->_edit_get_pivot());
 					} else {
 					} else {
 						drag_rotation_center = ci->get_global_transform_with_canvas().get_origin();
 						drag_rotation_center = ci->get_global_transform_with_canvas().get_origin();
@@ -1461,7 +1488,16 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
 				drag_to = transform.affine_inverse().xform(m->get_position());
 				drag_to = transform.affine_inverse().xform(m->get_position());
 				//Rotate the opposite way if the canvas item's compounded scale has an uneven number of negative elements
 				//Rotate the opposite way if the canvas item's compounded scale has an uneven number of negative elements
 				bool opposite = (ci->get_global_transform().get_scale().sign().dot(ci->get_transform().get_scale().sign()) == 0);
 				bool opposite = (ci->get_global_transform().get_scale().sign().dot(ci->get_transform().get_scale().sign()) == 0);
-				ci->_edit_set_rotation(snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), ci->_edit_get_rotation()));
+				real_t prev_rotation = ci->_edit_get_rotation();
+				real_t new_rotation = snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), prev_rotation);
+
+				ci->_edit_set_rotation(new_rotation);
+				if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
+					Transform2D xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse();
+					Vector2 radius = xform.xform(ci->_edit_get_position()) - temp_pivot;
+					radius = radius.rotated(new_rotation - prev_rotation);
+					ci->_edit_set_position(xform.affine_inverse().xform(temp_pivot + radius));
+				}
 				viewport->queue_redraw();
 				viewport->queue_redraw();
 			}
 			}
 			return true;
 			return true;
@@ -3161,7 +3197,7 @@ void CanvasItemEditor::_draw_ruler_tool() {
 	} else {
 	} else {
 		if (grid_snap_active) {
 		if (grid_snap_active) {
 			Ref<Texture2D> position_icon = get_editor_theme_icon(SNAME("EditorPosition"));
 			Ref<Texture2D> position_icon = get_editor_theme_icon(SNAME("EditorPosition"));
-			viewport->draw_texture(get_editor_theme_icon(SNAME("EditorPosition")), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2);
+			viewport->draw_texture(position_icon, (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2);
 		}
 		}
 	}
 	}
 }
 }
@@ -3583,6 +3619,10 @@ void CanvasItemEditor::_draw_selection() {
 				get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.6),
 				get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.6),
 				Math::round(2 * EDSCALE));
 				Math::round(2 * EDSCALE));
 	}
 	}
+
+	if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
+		viewport->draw_texture(pivot_icon, (temp_pivot - view_offset) * zoom - (pivot_icon->get_size() / 2).floor(), get_theme_color(SNAME("accent_color"), SNAME("Editor")));
+	}
 }
 }
 
 
 void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) {
 void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) {
@@ -3931,8 +3971,6 @@ void CanvasItemEditor::_notification(int p_what) {
 		} break;
 		} break;
 
 
 		case NOTIFICATION_PROCESS: {
 		case NOTIFICATION_PROCESS: {
-			int nb_having_pivot = 0;
-
 			// Update the viewport if the canvas_item changes
 			// Update the viewport if the canvas_item changes
 			List<CanvasItem *> selection = _get_edited_canvas_items(true);
 			List<CanvasItem *> selection = _get_edited_canvas_items(true);
 			for (CanvasItem *ci : selection) {
 			for (CanvasItem *ci : selection) {
@@ -3972,14 +4010,10 @@ void CanvasItemEditor::_notification(int p_what) {
 						viewport->queue_redraw();
 						viewport->queue_redraw();
 					}
 					}
 				}
 				}
-
-				if (ci->_edit_use_pivot()) {
-					nb_having_pivot++;
-				}
 			}
 			}
 
 
-			// Activate / Deactivate the pivot tool
-			pivot_button->set_disabled(nb_having_pivot == 0);
+			// Activate / Deactivate the pivot tool.
+			pivot_button->set_disabled(selection.is_empty());
 
 
 			// Update the viewport if bones changes
 			// Update the viewport if bones changes
 			for (KeyValue<BoneKey, BoneList> &E : bone_list) {
 			for (KeyValue<BoneKey, BoneList> &E : bone_list) {
@@ -4048,6 +4082,11 @@ void CanvasItemEditor::_selection_changed() {
 		_reset_drag();
 		_reset_drag();
 	}
 	}
 	selected_from_canvas = false;
 	selected_from_canvas = false;
+
+	if (temp_pivot != Vector2(INFINITY, INFINITY)) {
+		temp_pivot = Vector2(INFINITY, INFINITY);
+		viewport->queue_redraw();
+	}
 }
 }
 
 
 void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
 void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
@@ -4202,6 +4241,18 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
 
 
 	tool = (Tool)p_index;
 	tool = (Tool)p_index;
 
 
+	if (p_index == TOOL_EDIT_PIVOT && Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+		// Special action that places temporary rotation pivot in the middle of the selection.
+		List<CanvasItem *> selection = _get_edited_canvas_items();
+		if (!selection.is_empty()) {
+			Vector2 center;
+			for (const CanvasItem *ci : selection) {
+				center += ci->_edit_get_position();
+			}
+			temp_pivot = center / selection.size();
+		}
+	}
+
 	viewport->queue_redraw();
 	viewport->queue_redraw();
 	_update_cursor();
 	_update_cursor();
 }
 }
@@ -5279,7 +5330,7 @@ CanvasItemEditor::CanvasItemEditor() {
 	main_menu_hbox->add_child(pivot_button);
 	main_menu_hbox->add_child(pivot_button);
 	pivot_button->set_toggle_mode(true);
 	pivot_button->set_toggle_mode(true);
 	pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT));
 	pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT));
-	pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot."));
+	pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the rotation pivot in the center of the selected nodes."));
 
 
 	pan_button = memnew(Button);
 	pan_button = memnew(Button);
 	pan_button->set_theme_type_variation("FlatButton");
 	pan_button->set_theme_type_variation("FlatButton");

+ 2 - 0
editor/plugins/canvas_item_editor_plugin.h

@@ -174,6 +174,7 @@ private:
 		DRAG_SCALE_BOTH,
 		DRAG_SCALE_BOTH,
 		DRAG_ROTATE,
 		DRAG_ROTATE,
 		DRAG_PIVOT,
 		DRAG_PIVOT,
+		DRAG_TEMP_PIVOT,
 		DRAG_V_GUIDE,
 		DRAG_V_GUIDE,
 		DRAG_H_GUIDE,
 		DRAG_H_GUIDE,
 		DRAG_DOUBLE_GUIDE,
 		DRAG_DOUBLE_GUIDE,
@@ -251,6 +252,7 @@ private:
 	bool key_scale = false;
 	bool key_scale = false;
 
 
 	bool pan_pressed = false;
 	bool pan_pressed = false;
+	Vector2 temp_pivot = Vector2(INFINITY, INFINITY);
 
 
 	bool ruler_tool_active = false;
 	bool ruler_tool_active = false;
 	Point2 ruler_tool_origin;
 	Point2 ruler_tool_origin;