Skip to content

Commit 040e19e

Browse files
Calinouryevdokimov
andcommitted
Add "Follow Selection" in the 3D editor by using Center Selection twice
When pressing the Focus Selection shortcut twice, you will begin following the current selection. This also applies to selection changes. The effect is undone by pressing the Focus Selection shortcut another time, using the Focus Origin shortcut, or by panning/using freelook on the 3D editor camera. (Orbiting or switching between perspective and orthogonal does not undo the effect.) Co-authored-by: Robert Yevdokimov <robert.yevdokimov@autStand.com>
1 parent fbb918b commit 040e19e

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

editor/scene/3d/node_3d_editor_plugin.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
26942694
}
26952695
if (ED_IS_SHORTCUT("spatial_editor/focus_selection", event_mod)) {
26962696
_menu_option(VIEW_CENTER_TO_SELECTION);
2697+
times_focused_consecutively += 1;
26972698
}
26982699
if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", event_mod)) {
26992700
_menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW);
@@ -2875,6 +2876,8 @@ void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const
28752876
translation *= cursor.distance / DISTANCE_DEFAULT;
28762877
camera_transform.translate_local(translation);
28772878
cursor.pos = camera_transform.origin;
2879+
2880+
_disable_follow_mode();
28782881
}
28792882

28802883
void Node3DEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
@@ -3183,6 +3186,8 @@ void Node3DEditorViewport::_update_freelook(real_t delta) {
31833186
const Vector3 motion = direction * speed * delta;
31843187
cursor.pos += motion;
31853188
cursor.eye_pos += motion;
3189+
3190+
_disable_follow_mode();
31863191
}
31873192

31883193
void Node3DEditorViewport::set_message(const String &p_message, float p_time) {
@@ -3382,6 +3387,20 @@ void Node3DEditorViewport::_notification(int p_what) {
33823387

33833388
_update_freelook(delta);
33843389

3390+
if (focused_node_id.is_valid() && get_selected_count() > 0 && times_focused_consecutively >= 2 && times_focused_consecutively % 2 == 0) {
3391+
Node *focused_node = ObjectDB::get_instance<Node>(focused_node_id);
3392+
if (focused_node) {
3393+
follow_mode->set_text(vformat(TTR("Following %s"), focused_node->get_name()));
3394+
follow_mode->set_button_icon(get_editor_theme_icon(focused_node->get_class()));
3395+
follow_mode->show();
3396+
focus_selection();
3397+
} else {
3398+
_disable_follow_mode();
3399+
}
3400+
} else {
3401+
follow_mode->hide();
3402+
}
3403+
33853404
Node *scene_root = SceneTreeDock::get_singleton()->get_editor_data()->get_edited_scene_root();
33863405
if (previewing_cinema && scene_root != nullptr) {
33873406
Camera3D *cam = scene_root->get_viewport()->get_camera_3d();
@@ -3677,6 +3696,8 @@ void Node3DEditorViewport::_notification(int p_what) {
36773696
override_label_colors(view_display_menu);
36783697
override_button_stylebox(translation_preview_button, information_3d_stylebox);
36793698
override_label_colors(translation_preview_button);
3699+
override_button_stylebox(follow_mode, information_3d_stylebox);
3700+
override_label_colors(follow_mode);
36803701
override_button_stylebox(preview_camera, information_3d_stylebox);
36813702
override_label_colors(preview_camera);
36823703

@@ -4085,6 +4106,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
40854106
} break;
40864107
case VIEW_CENTER_TO_ORIGIN: {
40874108
cursor.pos = Vector3(0, 0, 0);
4109+
_disable_follow_mode();
40884110

40894111
} break;
40904112
case VIEW_CENTER_TO_SELECTION: {
@@ -4541,6 +4563,11 @@ void Node3DEditorViewport::_finish_gizmo_instances() {
45414563
RS::get_singleton()->free_rid(trackball_sphere_instance);
45424564
}
45434565

4566+
void Node3DEditorViewport::_disable_follow_mode() {
4567+
// Exit follow mode by resetting the number of times the follow shortcut was used consecutively.
4568+
times_focused_consecutively = 0;
4569+
}
4570+
45444571
void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) {
45454572
ERR_FAIL_COND(p_activate && !preview);
45464573
ERR_FAIL_COND(!p_activate && !previewing);
@@ -4965,6 +4992,10 @@ void Node3DEditorViewport::focus_selection() {
49654992
int count = 0;
49664993

49674994
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
4995+
focused_node_id = ObjectID();
4996+
if (!selection.is_empty()) {
4997+
focused_node_id = selection.front()->get()->get_instance_id();
4998+
}
49684999

49695000
for (Node *node : selection) {
49705001
Node3D *node_3d = Object::cast_to<Node3D>(node);
@@ -6392,6 +6423,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
63926423
view_display_menu->get_popup()->add_separator();
63936424
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
63946425
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
6426+
view_display_menu->get_popup()->set_item_tooltip(-1, TTR("Press Focus Selection twice to start following the selection as it moves. Press it yet another time to stop following the selection."));
63956427
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW);
63966428
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW);
63976429
view_display_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &Node3DEditorViewport::_menu_option));
@@ -6419,6 +6451,12 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
64196451
translation_preview_button = memnew(EditorTranslationPreviewButton);
64206452
hbox->add_child(translation_preview_button);
64216453

6454+
follow_mode = memnew(Button);
6455+
follow_mode->set_tooltip_text(TTR("Click to stop following this node as it moves."));
6456+
follow_mode->hide();
6457+
vbox->add_child(follow_mode);
6458+
follow_mode->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditorViewport::_disable_follow_mode));
6459+
64226460
preview_camera = memnew(CheckBox);
64236461
preview_camera->set_text(TTRC("Preview"));
64246462
// Using Control even on macOS to avoid conflict with Quick Open shortcut.

editor/scene/3d/node_3d_editor_plugin.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,12 @@ class Node3DEditorViewport : public Control {
244244
Node *target_node = nullptr;
245245
Point2 drop_pos;
246246

247+
ObjectID focused_node_id;
248+
247249
EditorSelection *editor_selection = nullptr;
248250

249251
Button *translation_preview_button = nullptr;
252+
Button *follow_mode = nullptr;
250253
CheckBox *preview_camera = nullptr;
251254
SubViewportContainer *subviewport_container = nullptr;
252255

@@ -502,10 +505,12 @@ class Node3DEditorViewport : public Control {
502505

503506
bool previewing_camera = false;
504507
bool previewing_cinema = false;
508+
int times_focused_consecutively = 0;
505509
bool _is_node_locked(const Node *p_node) const;
506510
void _preview_exited_scene();
507511
void _preview_camera_property_changed();
508512
void _update_centered_labels();
513+
void _disable_follow_mode();
509514
void _toggle_camera_preview(bool);
510515
void _toggle_cinema_preview(bool);
511516
void _init_gizmo_instance(int p_idx);

0 commit comments

Comments
 (0)