From 6d35dcf7c529a20f1ea100e770133e66b1c71086 Mon Sep 17 00:00:00 2001 From: Ricardo Buring Date: Wed, 19 Jun 2024 22:30:00 +0200 Subject: [PATCH] Physics interpolation: Fix 2D skinning Co-authored-by: lawnjelly --- scene/2d/skeleton_2d.cpp | 69 ++++++++++++++++++++++++++++++++++++---- scene/2d/skeleton_2d.h | 14 ++++++++ 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index fe21c7f21bd..8aa50668ebb 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -30,6 +30,8 @@ #include "skeleton_2d.h" +#include "core/math/transform_interpolator.h" + #ifdef TOOLS_ENABLED #include "editor/editor_data.h" #include "editor/editor_settings.h" @@ -634,6 +636,30 @@ Bone2D *Skeleton2D::get_bone(int p_idx) { return bones[p_idx].bone; } +void Skeleton2D::_update_process_mode() { + bool process = modification_stack.is_valid() && is_inside_tree(); + if (!process) { + // We might have another reason to process. + process = is_physics_interpolated_and_enabled() && is_visible_in_tree(); + } + + set_process_internal(process); + set_physics_process_internal(process); +} + +void Skeleton2D::_ensure_update_interpolation_data() { + 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 Skeleton2D::_physics_interpolated_changed() { + _update_process_mode(); +} + void Skeleton2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { @@ -646,17 +672,47 @@ void Skeleton2D::_notification(int p_what) { request_ready(); } break; + case NOTIFICATION_ENTER_TREE: { + _update_process_mode(); + + if (is_physics_interpolated_and_enabled()) { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; + } + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { - RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); + if (is_physics_interpolated_and_enabled()) { + _ensure_update_interpolation_data(); + if (Engine::get_singleton()->is_in_physics_frame()) { + _interpolation_data.xform_curr = get_global_transform(); + } + } else { + RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform()); + } + } break; + + case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { + _interpolation_data.xform_curr = get_global_transform(); + _interpolation_data.xform_prev = _interpolation_data.xform_curr; } break; case NOTIFICATION_INTERNAL_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + Transform2D res; + TransformInterpolator::interpolate_transform_2d(_interpolation_data.xform_prev, _interpolation_data.xform_curr, res, Engine::get_singleton()->get_physics_interpolation_fraction()); + RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, res); + } if (modification_stack.is_valid()) { execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process); } } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (is_physics_interpolated_and_enabled()) { + _ensure_update_interpolation_data(); + _interpolation_data.xform_curr = get_global_transform(); + } if (modification_stack.is_valid()) { execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process); } @@ -666,6 +722,10 @@ void Skeleton2D::_notification(int p_what) { set_modification_stack(modification_stack); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + _update_process_mode(); + } break; + #ifdef TOOLS_ENABLED case NOTIFICATION_DRAW: { if (Engine::get_singleton()->is_editor_hint()) { @@ -698,22 +758,17 @@ void Skeleton2D::set_modification_stack(Ref p_stack if (modification_stack.is_valid()) { modification_stack->is_setup = false; modification_stack->set_skeleton(nullptr); - - set_process_internal(false); - set_physics_process_internal(false); } modification_stack = p_stack; if (modification_stack.is_valid() && is_inside_tree()) { modification_stack->set_skeleton(this); modification_stack->setup(); - set_process_internal(true); - set_physics_process_internal(true); - #ifdef TOOLS_ENABLED modification_stack->set_editor_gizmos_dirty(true); #endif // TOOLS_ENABLED } + _update_process_mode(); } Ref Skeleton2D::get_modification_stack() const { diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h index ad6a47bf431..033bdff41d6 100644 --- a/scene/2d/skeleton_2d.h +++ b/scene/2d/skeleton_2d.h @@ -137,7 +137,21 @@ class Skeleton2D : public Node2D { Ref modification_stack; + /////////////////////////////////////////////////////// + // INTERPOLATION + struct InterpolationData { + Transform2D xform_curr; + Transform2D xform_prev; + uint32_t last_update_physics_tick = UINT32_MAX; // Ensure tick 0 is detected as a change. + } _interpolation_data; + + void _update_process_mode(); + void _ensure_update_interpolation_data(); + protected: + virtual void _physics_interpolated_changed() override; + /////////////////////////////////////////////////////// + void _notification(int p_what); static void _bind_methods(); bool _set(const StringName &p_path, const Variant &p_value);