mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 03:18:37 +08:00
Freelook interpolation fixes
- Smooth freelook position more explicitely - Don't let orbit zoom produce translation when it shouldn't - Make base speed framerate-independent (and tweaked setting for that) - Don't rely on camera for calculations because it no longer reflect immediate state - Avoid potential divide-by-zero with zoom inertia - Make speed/zoom relation optional (if enabled, speed is adjusted from zoom) - Never change zoom distance when freelook is active - Orbit inertia also applies on freelook
This commit is contained in:
parent
bd10a00240
commit
de42e53671
@ -719,12 +719,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
|
||||
// freelook
|
||||
_initial_set("editors/3d/freelook/freelook_inertia", 0.1);
|
||||
hints["editors/3d/freelook/freelook_inertia"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_inertia", PROPERTY_HINT_RANGE, "0.0, 1, 0.01");
|
||||
_initial_set("editors/3d/freelook/freelook_base_speed", 0.1);
|
||||
_initial_set("editors/3d/freelook/freelook_base_speed", 5.0);
|
||||
hints["editors/3d/freelook/freelook_base_speed"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_base_speed", PROPERTY_HINT_RANGE, "0.0, 10, 0.01");
|
||||
_initial_set("editors/3d/freelook/freelook_activation_modifier", 0);
|
||||
hints["editors/3d/freelook/freelook_activation_modifier"] = PropertyInfo(Variant::INT, "editors/3d/freelook/freelook_activation_modifier", PROPERTY_HINT_ENUM, "None,Shift,Alt,Meta,Ctrl");
|
||||
_initial_set("editors/3d/freelook/freelook_modifier_speed_factor", 3.0);
|
||||
hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1");
|
||||
_initial_set("editors/3d/freelook/freelook_speed_zoom_link", false);
|
||||
|
||||
_initial_set("editors/2d/bone_width", 5);
|
||||
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9));
|
||||
|
@ -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");
|
||||
|
||||
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)) || Input::get_singleton()->is_key_pressed(KEY_SHIFT) || Input::get_singleton()->is_key_pressed(KEY_ALT) || Input::get_singleton()->is_key_pressed(KEY_CONTROL);
|
||||
|
||||
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);
|
||||
|
||||
Cursor old_camera_cursor = camera_cursor;
|
||||
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)));
|
||||
if (p_interp_delta > 0) {
|
||||
|
||||
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)));
|
||||
//-------
|
||||
// Perform smoothing
|
||||
|
||||
if (p_interp_delta == 0 || is_freelook_active()) {
|
||||
camera_cursor = cursor;
|
||||
if (is_freelook_active()) {
|
||||
|
||||
// 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;
|
||||
|
||||
} 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);
|
||||
|
||||
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);
|
||||
|
||||
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()) {
|
||||
@ -1635,6 +1674,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)
|
||||
@ -1642,12 +1684,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();
|
||||
@ -1745,23 +1787,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();
|
||||
@ -1780,7 +1856,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;
|
||||
}
|
||||
|
||||
@ -1823,21 +1898,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) {
|
||||
@ -1876,7 +1945,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;
|
||||
@ -1887,7 +1956,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();
|
||||
|
||||
@ -3026,6 +3095,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);
|
||||
|
@ -131,7 +131,7 @@ private:
|
||||
float gizmo_scale;
|
||||
|
||||
bool freelook_active;
|
||||
Vector3 freelook_target_position;
|
||||
real_t freelook_speed;
|
||||
|
||||
PanelContainer *info;
|
||||
Label *info_label;
|
||||
@ -231,6 +231,7 @@ private:
|
||||
|
||||
Vector3 pos;
|
||||
float x_rot, y_rot, distance;
|
||||
Vector3 eye_pos; // Used in freelook mode
|
||||
bool region_select;
|
||||
Point2 region_begin, region_end;
|
||||
|
||||
@ -239,10 +240,17 @@ private:
|
||||
distance = 4;
|
||||
region_select = false;
|
||||
}
|
||||
} cursor, camera_cursor;
|
||||
};
|
||||
// Viewport camera supports movement smoothing,
|
||||
// so one cursor is the real cursor, while the other can be an interpolated version.
|
||||
Cursor cursor; // Immediate cursor
|
||||
Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes)
|
||||
|
||||
void scale_cursor_distance(real_t scale);
|
||||
|
||||
void set_freelook_active(bool active_now);
|
||||
void scale_freelook_speed(real_t scale);
|
||||
|
||||
real_t zoom_indicator_delay;
|
||||
|
||||
RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3];
|
||||
|
Loading…
Reference in New Issue
Block a user