From 63a2a65fc2528909f85b65110786b8065adfd373 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 21 Nov 2024 03:08:08 +0100 Subject: [PATCH] Add "Follow Selection" in the 3D editor by using Center Selection twice When pressing the Center Selection shortcut twice, you will begin following the current selection. This also applies to selection changes. The effect is undone by pressing the Center Selection shortcut another time, or by panning/using freelook on the 3D editor camera. (Orbiting does not undo the effect.) --- editor/plugins/node_3d_editor_plugin.cpp | 38 ++++++++++++++++++++++++ editor/plugins/node_3d_editor_plugin.h | 6 ++++ 2 files changed, 44 insertions(+) diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 810d1674ca17..1d5e18568b0b 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2317,21 +2317,27 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { } if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { _menu_option(VIEW_BOTTOM); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/top_view", p_event)) { _menu_option(VIEW_TOP); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/rear_view", p_event)) { _menu_option(VIEW_REAR); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/front_view", p_event)) { _menu_option(VIEW_FRONT); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/left_view", p_event)) { _menu_option(VIEW_LEFT); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) { _menu_option(VIEW_RIGHT); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) { // Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented. @@ -2362,9 +2368,11 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { } if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) { _menu_option(VIEW_CENTER_TO_ORIGIN); + _disable_follow_mode(); } if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) { _menu_option(VIEW_CENTER_TO_SELECTION); + times_focused_consecutively += 1; } if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) { _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW); @@ -2519,6 +2527,8 @@ void Node3DEditorViewport::_nav_pan(Ref p_event, const translation *= cursor.distance / DISTANCE_DEFAULT; camera_transform.translate_local(translation); cursor.pos = camera_transform.origin; + + _disable_follow_mode(); } void Node3DEditorViewport::_nav_zoom(Ref p_event, const Vector2 &p_relative) { @@ -2781,6 +2791,8 @@ void Node3DEditorViewport::_update_freelook(real_t delta) { const Vector3 motion = direction * speed * delta; cursor.pos += motion; cursor.eye_pos += motion; + + _disable_follow_mode(); } void Node3DEditorViewport::set_message(const String &p_message, float p_time) { @@ -2892,6 +2904,15 @@ void Node3DEditorViewport::_notification(int p_what) { _update_navigation_controls_visibility(); _update_freelook(delta); + if (focused_node && get_selected_count() > 0 && times_focused_consecutively >= 2 && times_focused_consecutively % 2 == 0) { + // Clearing the selection resets focus + follow_mode->set_text(vformat(TTR("Following %s"), focused_node->get_name())); + follow_mode->show(); + focus_selection(); + } else { + follow_mode->hide(); + } + Node *scene_root = SceneTreeDock::get_singleton()->get_editor_data()->get_edited_scene_root(); if (previewing_cinema && scene_root != nullptr) { Camera3D *cam = scene_root->get_viewport()->get_camera_3d(); @@ -3862,6 +3883,11 @@ void Node3DEditorViewport::_finish_gizmo_instances() { RS::get_singleton()->free(rotate_gizmo_instance[3]); } +void Node3DEditorViewport::_disable_follow_mode() { + // Exit follow mode by resetting the number of times the follow shortcut was used consecutively. + times_focused_consecutively = 0; +} + void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) { ERR_FAIL_COND(p_activate && !preview); ERR_FAIL_COND(!p_activate && !previewing); @@ -4231,6 +4257,10 @@ void Node3DEditorViewport::focus_selection() { int count = 0; const List &selection = editor_selection->get_selected_node_list(); + focused_node = nullptr; + if (!selection.is_empty()) { + focused_node = selection.get(0); + } for (Node *E : selection) { Node3D *sp = Object::cast_to(E); @@ -5510,6 +5540,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION); + view_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 exit following.")); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW); view_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &Node3DEditorViewport::_menu_option)); @@ -5565,6 +5596,13 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p ED_SHORTCUT("spatial_editor/instant_scale", TTR("Begin Scale Transformation")); ED_SHORTCUT("spatial_editor/collision_reposition", TTR("Reposition Using Collisions"), KeyModifierMask::SHIFT | Key::G); + follow_mode = memnew(Button); + follow_mode->set_tooltip_text(TTR("Click to stop following this node as it moves.")); + follow_mode->set_theme_type_variation("FlatButton"); + follow_mode->hide(); + vbox->add_child(follow_mode); + follow_mode->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditorViewport::_disable_follow_mode)); + preview_camera = memnew(CheckBox); preview_camera->set_text(TTR("Preview")); // Using Control even on macOS to avoid conflict with Quick Open shortcut. diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index d35fcb765377..a5e665e5c332 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -229,8 +229,11 @@ class Node3DEditorViewport : public Control { Node *target_node = nullptr; Point2 drop_pos; + Node *focused_node = nullptr; + EditorSelection *editor_selection = nullptr; + Button *follow_mode = nullptr; CheckBox *preview_camera = nullptr; SubViewportContainer *subviewport_container = nullptr; @@ -462,6 +465,7 @@ class Node3DEditorViewport : public Control { bool previewing_cinema = false; bool _is_node_locked(const Node *p_node) const; void _preview_exited_scene(); + void _disable_follow_mode(); void _toggle_camera_preview(bool); void _toggle_cinema_preview(bool); void _init_gizmo_instance(int p_idx); @@ -525,6 +529,8 @@ class Node3DEditorViewport : public Control { void focus_selection(); + int times_focused_consecutively = 0; + void assign_pending_data_pointers( Node3D *p_preview_node, AABB *p_preview_bounds,