diff options
Diffstat (limited to 'editor/plugins/spatial_editor_plugin.cpp')
| -rw-r--r-- | editor/plugins/spatial_editor_plugin.cpp | 260 |
1 files changed, 185 insertions, 75 deletions
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 32973db6e..547679b05 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -63,7 +63,8 @@ #define ZOOM_MULTIPLIER 1.08 #define ZOOM_INDICATOR_DELAY_S 1.5 -#define FREELOOK_MIN_SPEED 0.1 +#define FREELOOK_MIN_SPEED 0.01 +#define FREELOOK_SPEED_MULTIPLIER 1.08 #define MIN_Z 0.01 #define MAX_Z 10000 @@ -75,34 +76,66 @@ void SpatialEditorViewport::_update_camera(float p_interp_delta) { bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; - //when not being manipulated, move softly - float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); - float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); - //when being manipulated, move more quickly - float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); - float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); + Cursor old_camera_cursor = camera_cursor; + camera_cursor = cursor; - float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); + if (p_interp_delta > 0) { - //determine if being manipulated - bool manipulated = (Input::get_singleton()->get_mouse_button_mask() & (2 | 4)) || Input::get_singleton()->is_key_pressed(KEY_SHIFT) || Input::get_singleton()->is_key_pressed(KEY_ALT) || Input::get_singleton()->is_key_pressed(KEY_CONTROL); + //------- + // Perform smoothing - float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); - float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); + if (is_freelook_active()) { - Cursor old_camera_cursor = camera_cursor; - camera_cursor = cursor; + // Higher inertia should increase "lag" (lerp with factor between 0 and 1) + // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1. + real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia"); + inertia = MAX(0.001, inertia); + real_t factor = (1.0 / inertia) * p_interp_delta; + + // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos + camera_cursor.eye_pos = old_camera_cursor.eye_pos.linear_interpolate(cursor.eye_pos, CLAMP(factor, 0, 1)); + //camera_cursor.pos = camera_cursor.eye_pos + (cursor.pos - cursor.eye_pos); + + float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + orbit_inertia = MAX(0.0001, orbit_inertia); + camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + + Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1)); + camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance; - camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); - camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + } else { + + //when not being manipulated, move softly + float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia"); + float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia"); + //when being manipulated, move more quickly + float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia"); + float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia"); + + float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia"); + + //determine if being manipulated + bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4); + manipulated |= Input::get_singleton()->is_key_pressed(KEY_SHIFT); + manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT); + manipulated |= Input::get_singleton()->is_key_pressed(KEY_CONTROL); - camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); - camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); + float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia); + float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia); + zoom_inertia = MAX(0.0001, zoom_inertia); - if (p_interp_delta == 0 || is_freelook_active()) { - camera_cursor = cursor; + camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); + + camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); + camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); + } } + //------- + // Apply camera transform + float tolerance = 0.001; bool equal = true; if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) { @@ -845,11 +878,17 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { switch (b->get_button_index()) { case BUTTON_WHEEL_UP: { - scale_cursor_distance(is_freelook_active() ? zoom_factor : 1.0 / zoom_factor); + if (is_freelook_active()) + scale_freelook_speed(zoom_factor); + else + scale_cursor_distance(1.0 / zoom_factor); } break; case BUTTON_WHEEL_DOWN: { - scale_cursor_distance(is_freelook_active() ? 1.0 / zoom_factor : zoom_factor); + if (is_freelook_active()) + scale_freelook_speed(1.0 / zoom_factor); + else + scale_cursor_distance(zoom_factor); } break; case BUTTON_RIGHT: { @@ -901,10 +940,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (b->is_pressed()) { int mod = _get_key_modifier(b); if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { - freelook_active = true; + set_freelook_active(true); } } else { - freelook_active = false; + set_freelook_active(false); } if (freelook_active && !surface->has_focus()) { @@ -1645,6 +1684,9 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". + Transform prev_camera_transform = to_camera_transform(cursor); + cursor.x_rot += relative.y * radians_per_pixel; cursor.y_rot += relative.x * radians_per_pixel; if (cursor.x_rot > Math_PI / 2.0) @@ -1652,12 +1694,12 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (cursor.x_rot < -Math_PI / 2.0) cursor.x_rot = -Math_PI / 2.0; - // Look is like Orbit, except the cursor translates, not the camera + // Look is like the opposite of Orbit: the focus point rotates around the camera Transform camera_transform = to_camera_transform(cursor); Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); - Vector3 diff = camera->get_translation() - pos; + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; cursor.pos += diff; - freelook_target_position += diff; name = ""; _update_name(); @@ -1755,23 +1797,57 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } } +void SpatialEditorViewport::set_freelook_active(bool active_now) { + + if (!freelook_active && active_now) { + // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential + cursor = camera_cursor; + + // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos + Vector3 forward = to_camera_transform(cursor).basis.xform(Vector3(0, 0, -1)); + cursor.eye_pos = cursor.pos - cursor.distance * forward; + // Also sync the camera cursor, otherwise switching to freelook will be trippy if inertia is active + camera_cursor.eye_pos = cursor.eye_pos; + + if (EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_speed_zoom_link")) { + // Re-adjust freelook speed from the current zoom level + real_t base_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); + freelook_speed = base_speed * cursor.distance; + } + + } else if (freelook_active && !active_now) { + // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential + cursor = camera_cursor; + } + + freelook_active = active_now; +} + void SpatialEditorViewport::scale_cursor_distance(real_t scale) { // Prevents zero distance which would short-circuit any scaling if (cursor.distance < ZOOM_MIN_DISTANCE) cursor.distance = ZOOM_MIN_DISTANCE; - real_t prev_distance = cursor.distance; cursor.distance *= scale; if (cursor.distance < ZOOM_MIN_DISTANCE) cursor.distance = ZOOM_MIN_DISTANCE; - if (is_freelook_active()) { - // In freelook mode, cursor reference is reversed so it needs to be adjusted - Vector3 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1)); - cursor.pos += (cursor.distance - prev_distance) * forward; - } + zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; + surface->update(); +} + +void SpatialEditorViewport::scale_freelook_speed(real_t scale) { + + // Prevents zero distance which would short-circuit any scaling + if (freelook_speed < FREELOOK_MIN_SPEED) + freelook_speed = FREELOOK_MIN_SPEED; + + freelook_speed *= scale; + + if (freelook_speed < FREELOOK_MIN_SPEED) + freelook_speed = FREELOOK_MIN_SPEED; zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S; surface->update(); @@ -1790,7 +1866,6 @@ Point2i SpatialEditorViewport::_get_warped_mouse_motion(const Ref<InputEventMous void SpatialEditorViewport::_update_freelook(real_t delta) { if (!is_freelook_active()) { - freelook_target_position = cursor.pos; return; } @@ -1833,21 +1908,15 @@ void SpatialEditorViewport::_update_freelook(real_t delta) { speed_modifier = true; } - real_t inertia = EDITOR_DEF("editors/3d/freelook/freelook_inertia", 0.1); - inertia = MAX(0, inertia); - const real_t base_speed = EDITOR_DEF("editors/3d/freelook/freelook_base_speed", 0.5); - const real_t modifier_speed_factor = EDITOR_DEF("editors/3d/freelook/freelook_modifier_speed_factor", 3); - - real_t speed = base_speed * cursor.distance; - if (speed_modifier) + real_t speed = freelook_speed; + if (speed_modifier) { + real_t modifier_speed_factor = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_modifier_speed_factor"); speed *= modifier_speed_factor; + } - // Higher inertia should increase "lag" (lerp with factor between 0 and 1) - // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1. - - freelook_target_position += direction * speed; - real_t factor = (1.0 / (inertia + 0.001)) * delta; - cursor.pos = cursor.pos.linear_interpolate(freelook_target_position, CLAMP(factor, 0, 1)); + Vector3 motion = direction * speed * delta; + cursor.pos += motion; + cursor.eye_pos += motion; } void SpatialEditorViewport::set_message(String p_message, float p_time) { @@ -1886,7 +1955,7 @@ void SpatialEditorViewport::_notification(int p_what) { } */ - real_t delta = get_tree()->get_idle_process_time(); + real_t delta = get_process_delta_time(); if (zoom_indicator_delay > 0) { zoom_indicator_delay -= delta; @@ -1897,7 +1966,7 @@ void SpatialEditorViewport::_notification(int p_what) { _update_freelook(delta); - _update_camera(get_process_delta_time()); + _update_camera(delta); Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -2026,7 +2095,7 @@ void SpatialEditorViewport::_notification(int p_what) { } // TODO That should be part of the drawing API... -static void stroke_rect(CanvasItem *ci, Rect2 rect, Color color, real_t width = 1.0) { +static void stroke_rect(CanvasItem &ci, Rect2 rect, Color color, real_t width = 1.0) { // a---b // | | @@ -2036,10 +2105,31 @@ static void stroke_rect(CanvasItem *ci, Rect2 rect, Color color, real_t width = Vector2 c(rect.position.x, rect.position.y + rect.size.y); Vector2 d(rect.position + rect.size); - ci->draw_line(a, b, color, width); - ci->draw_line(b, d, color, width); - ci->draw_line(d, c, color, width); - ci->draw_line(c, a, color, width); + ci.draw_line(a, b, color, width); + ci.draw_line(b, d, color, width); + ci.draw_line(d, c, color, width); + ci.draw_line(c, a, color, width); +} + +static void draw_indicator_bar(Control &surface, real_t fill, Ref<Texture> icon) { + + // Adjust bar size from control height + Vector2 surface_size = surface.get_size(); + real_t h = surface_size.y / 2.0; + real_t y = (surface_size.y - h) / 2.0; + + Rect2 r(10, y, 6, h); + real_t sy = r.size.y * fill; + + // Note: because this bar appears over the viewport, it has to stay readable for any background color + // Draw both neutral dark and bright colors to account this + surface.draw_rect(r, Color(1, 1, 1, 0.2)); + surface.draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6)); + stroke_rect(surface, r.grow(1), Color(0, 0, 0, 0.7)); + + Vector2 icon_size = icon->get_size(); + Vector2 icon_pos = Vector2(r.position.x - (icon_size.x - r.size.x) / 2, r.position.y + r.size.y + 2); + surface.draw_texture(icon, icon_pos); } void SpatialEditorViewport::_draw() { @@ -2098,35 +2188,47 @@ void SpatialEditorViewport::_draw() { draw_rect = Rect2(Vector2(), s).clip(draw_rect); - stroke_rect(surface, draw_rect, Color(0.6, 0.6, 0.1, 0.5), 2.0); + stroke_rect(*surface, draw_rect, Color(0.6, 0.6, 0.1, 0.5), 2.0); } else { if (zoom_indicator_delay > 0.0) { - // Show indicative zoom factor - real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom? - real_t max_distance = camera->get_zfar(); - real_t scale_length = (max_distance - min_distance); + if (is_freelook_active()) { + // Show speed - if (Math::abs(scale_length) > CMP_EPSILON) { - real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); + real_t min_speed = FREELOOK_MIN_SPEED; + real_t max_speed = camera->get_zfar(); + real_t scale_length = (max_speed - min_speed); - // There is no real maximum distance so that factor can become negative, - // Let's make it look asymptotic instead (will decrease slower and slower). - if (logscale_t < 0.25) - logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); + if (Math::abs(scale_length) > CMP_EPSILON) { + real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); - Vector2 surface_size = surface->get_size(); - real_t h = surface_size.y / 2.0; - real_t y = (surface_size.y - h) / 2.0; + // There is no real maximum speed so that factor can become negative, + // Let's make it look asymptotic instead (will decrease slower and slower). + if (logscale_t < 0.25) + logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); - Rect2 r(10, y, 6, h); - real_t sy = r.size.y * logscale_t; + draw_indicator_bar(*surface, 1.0 - logscale_t, get_icon("ViewportSpeed", "EditorIcons")); + } + + } else { + // Show zoom - surface->draw_rect(r, Color(1, 1, 1, 0.2)); - surface->draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6)); - stroke_rect(surface, r.grow(1), Color(0, 0, 0, 0.7)); + real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom? + real_t max_distance = camera->get_zfar(); + real_t scale_length = (max_distance - min_distance); + + if (Math::abs(scale_length) > CMP_EPSILON) { + real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); + + // There is no real maximum distance so that factor can become negative, + // Let's make it look asymptotic instead (will decrease slower and slower). + if (logscale_t < 0.25) + logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0); + + draw_indicator_bar(*surface, logscale_t, get_icon("ViewportZoom", "EditorIcons")); + } } } } @@ -2576,6 +2678,7 @@ void SpatialEditorViewport::reset() { cursor.y_rot = 0.5; cursor.distance = 4; cursor.region_select = false; + cursor.pos = Vector3(); _update_name(); } @@ -2662,8 +2765,14 @@ Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const normal = hit_normal; } } - Vector3 center = preview_bounds->get_size() * 0.5; - return point + (center * normal); + Vector3 offset = Vector3(); + for (int i = 0; i < 3; i++) { + if (normal[i] > 0.0) + offset[i] = (preview_bounds->get_size()[i] - (preview_bounds->get_size()[i] + preview_bounds->get_position()[i])); + else if (normal[i] < 0.0) + offset[i] = -(preview_bounds->get_size()[i] + preview_bounds->get_position()[i]); + } + return point + offset; } Rect3 SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, const Rect3 p_bounds) { @@ -3036,6 +3145,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed accept = NULL; freelook_active = false; + freelook_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed"); selection_menu = memnew(PopupMenu); add_child(selection_menu); |
