From 90bddd21c81bd2e4cf35cdf731070ce8334fef30 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Thu, 24 Mar 2022 08:31:09 -0400 Subject: [PATCH] Wrap mouse for blender-style transforms. When performing a blender-style translate or scale (but not rotate), wrap the mouse inside the bounds of the viewport. This allows moving the mouse indefinitely for large translate or scale operations, and is consistent with how blender works. This enables global input processing during blender-style transforms, so we can capture and wrap mouse events outside the bounds of the viewport. Other wrapping operations use _sinput, which I assume is possible because a mouse button is held, so the control receives input even when the mouse exits the control. This does not implement wrapping for regular (gizmo-driven) transforms, though this could be done if desired. Fixes godotengine/godot-proposals#4255. --- editor/plugins/node_3d_editor_plugin.cpp | 36 +++++++++++++++++++----- editor/plugins/node_3d_editor_plugin.h | 3 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 4b65ce73041..c5715565367 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1583,6 +1583,22 @@ void Node3DEditorViewport::_list_select(Ref b) { } } +// This is only active during instant transforms, +// to capture and wrap mouse events outside the control. +void Node3DEditorViewport::input(const Ref &p_event) { + ERR_FAIL_COND(!_edit.instant); + Ref m = p_event; + + if (m.is_valid()) { + if (_edit.mode == TRANSFORM_ROTATE) { + _edit.mouse_pos = m->get_position(); // rotate should not wrap + } else { + _edit.mouse_pos += _get_warped_mouse_motion(p_event); + } + update_transform(_get_key_modifier(m) == Key::SHIFT); + } +} + void Node3DEditorViewport::_sinput(const Ref &p_event) { if (previewing) { return; //do NONE @@ -1906,7 +1922,8 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { Ref m = p_event; - if (m.is_valid()) { + // Instant transforms process mouse motion in input() to handle wrapping. + if (m.is_valid() && !_edit.instant) { _edit.mouse_pos = m->get_position(); if (spatial_editor->get_single_selected_node()) { @@ -1956,7 +1973,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle, _edit.gizmo_handle_secondary); set_message(n + ": " + String(v)); - } else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT) || _edit.instant) { + } else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT)) { if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) { nav_mode = NAVIGATION_ORBIT; } else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) { @@ -1993,7 +2010,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { return; } - update_transform(m->get_position(), _get_key_modifier(m) == Key::SHIFT); + update_transform(_get_key_modifier(m) == Key::SHIFT); } } else if (m->get_button_mask().has_flag(MouseButtonMask::RIGHT) || freelook_active) { if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) { @@ -2183,7 +2200,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) { _edit.plane = TRANSFORM_VIEW; spatial_editor->set_local_coords_enabled(false); } - update_transform(_edit.mouse_pos, Input::get_singleton()->is_key_pressed(Key::SHIFT)); + update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT)); set_message(new_message, 2); accept_event(); return; @@ -4515,9 +4532,11 @@ void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) { _edit.instant = instant; _edit.snap = spatial_editor->is_snap_enabled(); update_transform_gizmo_view(); + set_process_input(instant); } } +// Apply the current transform operation. void Node3DEditorViewport::commit_transform() { ERR_FAIL_COND(_edit.mode == TRANSFORM_NONE); static const char *_transform_name[4] = { @@ -4552,9 +4571,10 @@ void Node3DEditorViewport::commit_transform() { set_message(""); } -void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { - Vector3 ray_pos = _get_ray_pos(p_mousepos); - Vector3 ray = _get_ray(p_mousepos); +// Update the current transform operation in response to an input. +void Node3DEditorViewport::update_transform(bool p_shift) { + Vector3 ray_pos = _get_ray_pos(_edit.mouse_pos); + Vector3 ray = _get_ray(_edit.mouse_pos); double snap = EDITOR_GET("interface/inspector/default_float_step"); int snap_step_decimals = Math::range_step_decimals(snap); @@ -4894,12 +4914,14 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { } } +// Perform cleanup after a transform operation is committed or cancelled. void Node3DEditorViewport::finish_transform() { spatial_editor->set_local_coords_enabled(_edit.original_local); _edit.mode = TRANSFORM_NONE; _edit.instant = false; spatial_editor->update_transform_gizmo(); surface->queue_redraw(); + set_process_input(false); } // Register a shortcut and also add it as an input action with the same events. diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 28803e67cbb..79674bdd646 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -401,6 +401,7 @@ private: void _surface_focus_enter(); void _surface_focus_exit(); + void input(const Ref &p_event) override; void _sinput(const Ref &p_event); void _update_freelook(real_t delta); Node3DEditor *spatial_editor = nullptr; @@ -444,7 +445,7 @@ private: void begin_transform(TransformMode p_mode, bool instant); void commit_transform(); - void update_transform(Point2 p_mousepos, bool p_shift); + void update_transform(bool p_shift); void finish_transform(); void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false);