Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] 2D Fixed Timestep Interpolation #76252

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions core/math/transform_interpolator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,51 @@

#include "transform_interpolator.h"

#include "core/math/transform_2d.h"

void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction) {
// Extract parameters.
Vector2 p1 = p_prev.get_origin();
Vector2 p2 = p_curr.get_origin();

// Special case for physics interpolation, if flipping, don't interpolate basis.
// If the determinant polarity changes, the handedness of the coordinate system changes.
if (_sign(p_prev.determinant()) != _sign(p_curr.determinant())) {
r_result.elements[0] = p_curr.elements[0];
r_result.elements[1] = p_curr.elements[1];
r_result.set_origin(Vector2::linear_interpolate(p1, p2, p_fraction));
return;
}

real_t r1 = p_prev.get_rotation();
real_t r2 = p_curr.get_rotation();

Size2 s1 = p_prev.get_scale();
Size2 s2 = p_curr.get_scale();

// Slerp rotation.
Vector2 v1(Math::cos(r1), Math::sin(r1));
Vector2 v2(Math::cos(r2), Math::sin(r2));

real_t dot = v1.dot(v2);

dot = CLAMP(dot, -1, 1);

Vector2 v;

if (dot > 0.9995f) {
v = Vector2::linear_interpolate(v1, v2, p_fraction).normalized(); // Linearly interpolate to avoid numerical precision issues.
} else {
real_t angle = p_fraction * Math::acos(dot);
Vector2 v3 = (v2 - v1 * dot).normalized();
v = v1 * Math::cos(angle) + v3 * Math::sin(angle);
}

// Construct matrix.
r_result = Transform2D(Math::atan2(v.y, v.x), Vector2::linear_interpolate(p1, p2, p_fraction));
r_result.scale_basis(Vector2::linear_interpolate(s1, s2, p_fraction));
}

void TransformInterpolator::interpolate_transform(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction) {
r_result.origin = p_prev.origin + ((p_curr.origin - p_prev.origin) * p_fraction);
interpolate_basis(p_prev.basis, p_curr.basis, r_result.basis, p_fraction);
Expand Down
4 changes: 3 additions & 1 deletion core/math/transform_interpolator.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
// several frames may occur between each physics tick, which will make it cheaper
// than performing every frame.

class Transform;
struct Transform2D;

class TransformInterpolator {
public:
Expand All @@ -66,6 +66,7 @@ class TransformInterpolator {
static Quat _basis_to_quat_unchecked(const Basis &p_basis);
static bool _basis_is_orthogonal(const Basis &p_basis, real_t p_epsilon = 0.01f);
static bool _basis_is_orthogonal_any_scale(const Basis &p_basis);
static bool _sign(real_t p_val) { return p_val >= 0; }

static void interpolate_basis_linear(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction);
static void interpolate_basis_scaled_slerp(Basis p_prev, Basis p_curr, Basis &r_result, real_t p_fraction);
Expand All @@ -75,6 +76,7 @@ class TransformInterpolator {
// These will be slower.
static void interpolate_transform(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction);
static void interpolate_basis(const Basis &p_prev, const Basis &p_curr, Basis &r_result, real_t p_fraction);
static void interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction);

// Optimized function when you know ahead of time the method
static void interpolate_transform_via_method(const Transform &p_prev, const Transform &p_curr, Transform &r_result, real_t p_fraction, Method p_method);
Expand Down
1 change: 1 addition & 0 deletions core/os/main_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class MainLoop : public Object {
virtual void input_text(const String &p_text);

virtual void init();
virtual void iteration_prepare() {}
virtual bool iteration(float p_time);
virtual void iteration_end() {}
virtual bool idle(float p_time);
Expand Down
1 change: 1 addition & 0 deletions doc/classes/Control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@
<member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" enum="Control.MouseFilter" default="0">
Controls whether the control will be able to receive mouse button input events through [method _gui_input] and how these events should be handled. Also controls whether the control can receive the [signal mouse_entered], and [signal mouse_exited] signals. See the constants to learn what each does.
</member>
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
<member name="rect_clip_content" type="bool" setter="set_clip_contents" getter="is_clipping_contents" default="false">
Enables whether rendering of [CanvasItem] based children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered.
</member>
Expand Down
1 change: 1 addition & 0 deletions doc/classes/ParallaxLayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<member name="motion_scale" type="Vector2" setter="set_motion_scale" getter="get_motion_scale" default="Vector2( 1, 1 )">
Multiplies the ParallaxLayer's motion. If an axis is set to [code]0[/code], it will not scroll.
</member>
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="1" />
</members>
<constants>
</constants>
Expand Down
78 changes: 78 additions & 0 deletions doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@
Once finished with your RID, you will want to free the RID using the VisualServer's [method free_rid] static method.
</description>
</method>
<method name="canvas_item_reset_physics_interpolation">
<return type="void" />
<argument index="0" name="item" type="RID" />
<description>
Prevents physics interpolation for the current physics tick.
This is useful when moving a canvas item to a new location, to give an instantaneous change rather than interpolation from the previous location.
</description>
</method>
<method name="canvas_item_set_clip">
<return type="void" />
<argument index="0" name="item" type="RID" />
Expand Down Expand Up @@ -375,6 +383,14 @@
Sets the index for the [CanvasItem].
</description>
</method>
<method name="canvas_item_set_interpolated">
<return type="void" />
<argument index="0" name="item" type="RID" />
<argument index="1" name="interpolated" type="bool" />
<description>
Turns on and off physics interpolation for the canvas item.
</description>
</method>
<method name="canvas_item_set_light_mask">
<return type="void" />
<argument index="0" name="item" type="RID" />
Expand Down Expand Up @@ -463,6 +479,16 @@
Sets the [CanvasItem]'s Z index, i.e. its draw order (lower indexes are drawn first).
</description>
</method>
<method name="canvas_item_transform_physics_interpolation">
<return type="void" />
<argument index="0" name="item" type="RID" />
<argument index="1" name="xform" type="Transform2D" />
<description>
Transforms both the current and previous stored transform for a canvas item.
This allows transforming a canvas item without creating a "glitch" in the interpolation.
This is particularly useful for large worlds utilising a shifting origin.
</description>
</method>
<method name="canvas_light_attach_to_canvas">
<return type="void" />
<argument index="0" name="light" type="RID" />
Expand Down Expand Up @@ -493,6 +519,14 @@
Once finished with your RID, you will want to free the RID using the VisualServer's [method free_rid] static method.
</description>
</method>
<method name="canvas_light_occluder_reset_physics_interpolation">
<return type="void" />
<argument index="0" name="occluder" type="RID" />
<description>
Prevents physics interpolation for the current physics tick.
This is useful when moving an occluder to a new location, to give an instantaneous change rather than interpolation from the previous location.
</description>
</method>
<method name="canvas_light_occluder_set_enabled">
<return type="void" />
<argument index="0" name="occluder" type="RID" />
Expand All @@ -501,6 +535,14 @@
Enables or disables light occluder.
</description>
</method>
<method name="canvas_light_occluder_set_interpolated">
<return type="void" />
<argument index="0" name="occluder" type="RID" />
<argument index="1" name="interpolated" type="bool" />
<description>
Turns on and off physics interpolation for the occluder.
</description>
</method>
<method name="canvas_light_occluder_set_light_mask">
<return type="void" />
<argument index="0" name="occluder" type="RID" />
Expand All @@ -525,6 +567,24 @@
Sets a light occluder's [Transform2D].
</description>
</method>
<method name="canvas_light_occluder_transform_physics_interpolation">
<return type="void" />
<argument index="0" name="occluder" type="RID" />
<argument index="1" name="xform" type="Transform2D" />
<description>
Transforms both the current and previous stored transform for an occluder.
This allows transforming an occluder without creating a "glitch" in the interpolation.
This is particularly useful for large worlds utilising a shifting origin.
</description>
</method>
<method name="canvas_light_reset_physics_interpolation">
<return type="void" />
<argument index="0" name="light" type="RID" />
<description>
Prevents physics interpolation for the current physics tick.
This is useful when moving a light to a new location, to give an instantaneous change rather than interpolation from the previous location.
</description>
</method>
<method name="canvas_light_set_color">
<return type="void" />
<argument index="0" name="light" type="RID" />
Expand Down Expand Up @@ -557,6 +617,14 @@
Sets a canvas light's height.
</description>
</method>
<method name="canvas_light_set_interpolated">
<return type="void" />
<argument index="0" name="light" type="RID" />
<argument index="1" name="interpolated" type="bool" />
<description>
Turns on and off physics interpolation for the light.
</description>
</method>
<method name="canvas_light_set_item_cull_mask">
<return type="void" />
<argument index="0" name="light" type="RID" />
Expand Down Expand Up @@ -679,6 +747,16 @@
Sets the Z range of objects that will be affected by this light. Equivalent to [member Light2D.range_z_min] and [member Light2D.range_z_max].
</description>
</method>
<method name="canvas_light_transform_physics_interpolation">
<return type="void" />
<argument index="0" name="light" type="RID" />
<argument index="1" name="xform" type="Transform2D" />
<description>
Transforms both the current and previous stored transform for a light.
This allows transforming a light without creating a "glitch" in the interpolation.
This is particularly useful for large worlds utilising a shifting origin.
</description>
</method>
<method name="canvas_occluder_polygon_create">
<return type="RID" />
<description>
Expand Down
6 changes: 6 additions & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,12 @@ bool Main::iteration() {

PhysicsServer::get_singleton()->flush_queries();

// Prepare the fixed timestep interpolated nodes
// BEFORE they are updated by the physics 2D,
// otherwise the current and previous transforms
// may be the same, and no interpolation takes place.
OS::get_singleton()->get_main_loop()->iteration_prepare();

Physics2DServer::get_singleton()->sync();
Physics2DServer::get_singleton()->flush_queries();

Expand Down
Loading