Fixed Timestep Interpolation (2D)
Adds fixed timestep interpolation to the rendering server (2D only). Switchable on and off with a project setting (default is off). Co-authored-by: lawnjelly <lawnjelly@gmail.com>
This commit is contained in:
@@ -54,7 +54,12 @@ void Camera2D::_update_scroll() {
|
||||
if (is_current()) {
|
||||
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
|
||||
|
||||
Transform2D xform = get_camera_transform();
|
||||
Transform2D xform;
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
xform = _interpolation_data.xform_prev.interpolate_with(_interpolation_data.xform_curr, Engine::get_singleton()->get_physics_interpolation_fraction());
|
||||
} else {
|
||||
xform = get_camera_transform();
|
||||
}
|
||||
|
||||
viewport->set_canvas_transform(xform);
|
||||
|
||||
@@ -68,15 +73,26 @@ void Camera2D::_update_scroll() {
|
||||
}
|
||||
|
||||
void Camera2D::_update_process_callback() {
|
||||
if (_is_editing_in_editor()) {
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
set_process_internal(is_current());
|
||||
set_physics_process_internal(is_current());
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (process_callback == CAMERA2D_PROCESS_IDLE) {
|
||||
WARN_PRINT_ONCE("Camera2D overridden to physics process mode due to use of physics interpolation.");
|
||||
}
|
||||
#endif
|
||||
} else if (_is_editing_in_editor()) {
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(false);
|
||||
} else if (process_callback == CAMERA2D_PROCESS_IDLE) {
|
||||
set_process_internal(true);
|
||||
set_physics_process_internal(false);
|
||||
} else {
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(true);
|
||||
if (process_callback == CAMERA2D_PROCESS_IDLE) {
|
||||
set_process_internal(true);
|
||||
set_physics_process_internal(false);
|
||||
} else {
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +177,15 @@ Transform2D Camera2D::get_camera_transform() {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: There is a bug here, introduced before physics interpolation.
|
||||
// Smoothing occurs rather confusingly during the call to get_camera_transform().
|
||||
// It may be called MULTIPLE TIMES on certain frames,
|
||||
// therefore smoothing is not currently applied only once per frame / tick,
|
||||
// which will result in some haphazard results.
|
||||
if (position_smoothing_enabled && !_is_editing_in_editor()) {
|
||||
real_t c = position_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time());
|
||||
bool physics_process = (process_callback == CAMERA2D_PROCESS_PHYSICS) || is_physics_interpolated_and_enabled();
|
||||
real_t delta = physics_process ? get_physics_process_delta_time() : get_process_delta_time();
|
||||
real_t c = position_smoothing_speed * delta;
|
||||
smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;
|
||||
ret_camera_pos = smoothed_camera_pos;
|
||||
//camera_pos=camera_pos*(1.0-position_smoothing_speed)+new_camera_pos*position_smoothing_speed;
|
||||
@@ -223,19 +246,54 @@ Transform2D Camera2D::get_camera_transform() {
|
||||
return xform.affine_inverse();
|
||||
}
|
||||
|
||||
void Camera2D::_ensure_update_interpolation_data() {
|
||||
// The "curr -> previous" update can either occur
|
||||
// on NOTIFICATION_INTERNAL_PHYSICS_PROCESS, OR
|
||||
// on NOTIFICATION_TRANSFORM_CHANGED,
|
||||
// if NOTIFICATION_TRANSFORM_CHANGED takes place earlier than
|
||||
// NOTIFICATION_INTERNAL_PHYSICS_PROCESS on a tick.
|
||||
// This is to ensure that the data keeps flowing, but the new data
|
||||
// doesn't overwrite before prev has been set.
|
||||
|
||||
// Keep the data flowing.
|
||||
uint64_t tick = Engine::get_singleton()->get_physics_frames();
|
||||
if (_interpolation_data.last_update_physics_tick != tick) {
|
||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
||||
_interpolation_data.last_update_physics_tick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_INTERNAL_PROCESS:
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
_update_scroll();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
if (!position_smoothing_enabled || _is_editing_in_editor()) {
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
_ensure_update_interpolation_data();
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
} else {
|
||||
_update_scroll();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
||||
// Force the limits etc. to update.
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
if ((!position_smoothing_enabled && !is_physics_interpolated_and_enabled()) || _is_editing_in_editor()) {
|
||||
_update_scroll();
|
||||
}
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
_ensure_update_interpolation_data();
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
|
||||
@@ -260,6 +318,15 @@ void Camera2D::_notification(int p_what) {
|
||||
_update_process_callback();
|
||||
first = true;
|
||||
_update_scroll();
|
||||
|
||||
// Note that NOTIFICATION_RESET_PHYSICS_INTERPOLATION
|
||||
// is automatically called before this because Camera2D is inherited
|
||||
// from CanvasItem. However, the camera transform is not up to date
|
||||
// until this point, so we do an extra manual reset.
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
@@ -431,12 +498,17 @@ void Camera2D::_make_current(Object *p_which) {
|
||||
|
||||
queue_redraw();
|
||||
|
||||
if (p_which == this) {
|
||||
bool was_current = viewport->get_camera_2d() == this;
|
||||
bool is_current = p_which == this;
|
||||
|
||||
if (is_current) {
|
||||
viewport->_camera_2d_set(this);
|
||||
} else {
|
||||
if (viewport->get_camera_2d() == this) {
|
||||
viewport->_camera_2d_set(nullptr);
|
||||
}
|
||||
} else if (was_current) {
|
||||
viewport->_camera_2d_set(nullptr);
|
||||
}
|
||||
|
||||
if (is_current != was_current) {
|
||||
_update_process_callback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,6 +528,7 @@ void Camera2D::make_current() {
|
||||
_make_current(this);
|
||||
}
|
||||
_update_scroll();
|
||||
_update_process_callback();
|
||||
}
|
||||
|
||||
void Camera2D::clear_current() {
|
||||
@@ -468,6 +541,8 @@ void Camera2D::clear_current() {
|
||||
if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
|
||||
viewport->assign_next_enabled_camera_2d(group_name);
|
||||
}
|
||||
|
||||
_update_process_callback();
|
||||
}
|
||||
|
||||
bool Camera2D::is_current() const {
|
||||
|
||||
Reference in New Issue
Block a user