godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

Add physics step interpolation in 3D (implemented in 2D since 4.3) #2753

Closed reduz closed 3 weeks ago

reduz commented 3 years ago

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

The following are common scenarios where users often find trouble in Godot:

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Interpolation of code that runs in the physics step is generally desired, but implementing this is not easy. The main limitation is that, when using interpolation, one must discern between motion and teleporting. Failure to specify this will result in the wrong behavior.

Additionally, "automating" things too much (make everything interpolation by default) can also be troublesome and lead to unexpected results.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

After through analysis of the use cases, it is clear that the places where actual interpolation is desired are as follows:

With this in mind, we can probably do some engine changes to make this process more or less transparent to the use. This would begin by implementing two new methods:

Node3D::set_transform_interpolated(const Transform& p_transform)
CanvasItem::set_transform_interpolated(const Transform2D& p_transform)

(we can also add translate interpolate, rotate interpolate and scale interpolate, which would call this method).

Calling these methods outside of the physics ticks will result in an error (in case user misunderstand what these are for).

In any case, calling these will work not only on the current node but also on the sub-tree, so motion of children nodes will also be interpolated. Basically, all these will call set_transform for RenderingServer 3D and 2D instances with an extra flag telling them that its interpolated, then the RenderingServer will internally perform the interpolation. Additionally, Camera3D and Camera2D will have this method too in the rendering server, since their motion can be interpolated.

The idea is that move_and_slide, RigidBody, etc. will internally all use this method so by default the user does not need to do anything and everything will just work. Camera2D and Camera3D are generally children of the character in Godot, so they will naturally know when their transform was interpolated, for which this requires no changes from the user.

So, yes, the idea is that this is implemented as transparent and possible, so existing games don't need to call the interpolated transform, and games just work, and complexity to develop by having to discern between motion and teleport does not happen.

FAQ

Q: So, I can interpolate manually by calling set_interpolated_transform, but I want to teleport an object, how do I do this? A: By default nothing is interpolated except the cases mentioned above, so setting position manually is equivalent to teleporting.

Q: Does this solve jitter in high refresh rate monitors? A: While this mostly works now, this new implementation will out of the box improve the quality and physics motion in high refresh rate monitors.

Q: Does using this have known shortcomings? A: Traditionally, the physics steps happens left often than the refresh, which makes it have a small amount of latency, so using the new position as target interpolation will add a physics frame of latency. This is unavoidable. The alternative would be to instead set a linear and angular velocity, but when running your game at low physics FPS (for networking or saving mobile GPU), overshooting becomes visible, so its not viable in this case.

Q: Interpolation is done in the RenderingServer, why not in the node? A:: This is desired for performance, its much cheaper to do the interpolation in the rendering server. The only shortcoming is that, in 3D, interpolation happens in global space rather in local space, so objects rotating around a pivot will introduce some motion aliasing (mostly noticeable in 3D games with very lock physics FPS. In 2D pivot information is retained in the server, so this is not a problem). That said, this should not be a big problem, as when running at low physics tick rate, motion aliasing is present almost everywhere. If you use low physics FPS you should be aware of its effects.

Q: Will cubic/hermite/etc interpolation be supported? A: These interpolations are troublesome in many ways. Mostly because when rotating, velocity is not constant (objects can accelerate/decelerate), and trajectories can easily overshoot (go past, then go back). Additionally as an extra sample is needed, an extra physics frame of lag is added. So, in general this will most likely not be supported.

Q: Will I need to change my game too much to take advantage of it? A: I believe with the proposed changes, most games should be able to take advantage of this out of the box.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Is there a reason why this should be core and not an add-on in the asset library?

There are some scripts to do this, but its clearly not ideal, needs to be core.

nonunknown commented 3 years ago

@lawnjelly made a solution for this! https://github.com/lawnjelly/smoothing-addon the only downside of his plugin, is that you need to reestructure the way you compose your scene, maybe a improved version of it, whithout requiring changing the scene structure? this will make easy for porting old versions of the game!

Seubmarine commented 3 years ago

Could this pr solve #961 or be able to add this feature more easily ?

reduz commented 3 years ago

@nonunknown this proposal is so you can achieve the same without doing absolutely anything.

reduz commented 3 years ago

@Seubmarine At some point I think I fixed this specifically, so I am surprised it remains broken. Will have to give it a check. This is unrelated, though.

GarbajYT commented 3 years ago

I usually deal with physics jitter with a just a few lines of code https://github.com/GarbajYT/godot_updated_fps_controller/blob/main/FPS_controller_3.3/FPS.gd but I think there is some value in having an "it just works" button

theraot commented 3 years ago

What about move_and_slide_with_snap and move_and_collide? Which are not mentioned at the time of writing.

I guess it makes sense that move_and_slide_with_snap also interpolates by default the same as move_and_slide.

Will move_and_collide be considered a teleport? Considering that it is already different from move_and_slide in that it does not take a velocity. That would mean remembering to set the interpolation when using it…

Which brings another question: Do I need to know the final transform before hand to set the interpolation?

If that is the case, I'd appreciate an improved test_move that tells me the distance it can move, not only if it collides. So I can call test_move to get the distance and then set the interpolation for that distance.

AndreaCatania commented 3 years ago

This seems great, and maybe we will be able to introduce some classes to customize it and allow much sophisticated interpolation mechanism, later on.

Regarding the interpolation, once the transform is set using set_global_transform_interpolated / set_transform_interpolated, if the function get_global_transform is executed to that node or one of the child nodes, what transform it returns? The interpolated one or the "static" one? This is important for networking, that functions should always return the static transform, as is now.

I like the idea to put this inside the renderer directly, this will speedup the interpolation a lot especially because we will likely need a class that calculates the interpolation speed, and if this is done directly on the rendering side we can do it once for all the nodes, which is great! The above class is needed because the rendering framerate is dynamic and its oscillations will introduce some delays. So we need a class that sometimes speedup and slowdown the interpolation depending on the frame rate, so the interpolation is always smooth and uptodate. Here, I wrote something https://github.com/godotengine/godot-proposals/issues/671#issuecomment-701323162, for sure you can do better and more universal, though here the link anyway. The class is: FrameInterpolator.

Nice, I like this feature and how it's turning out.

SaracenOne commented 3 years ago

This seems like a good proposal. We already make use of Lawnjelly's own interpolated node addon, and I think he also wanted to provide a more native implementation of the same functionality. It makes a ton of sense particularly for VR where you have wildly varying refresh rates. The latency is unfortunate, but feels like a necessary tradeoff.

reduz commented 3 years ago

@AndreaCatania

If the function get_global_transform is executed to that node or one of the child nodes, what transform it returns? The interpolated one or the "static" one?

Indeed, it should return the static one, the scene nodes will not do anything interpolation related other than mark the rendering server that the transform includes interpolation.

jknightdoeswork commented 3 years ago

https://github.com/jknightdoeswork/goodoh/blob/master/nodes/InterpolatedTransform.gd

Above is the Script I have used to interpolate transforms. In my opinion, it is simpler than Lawn Kelly's add-on.

An alternative API for this proposal is to have a boolean on a node (is_interpolated) which, when enabled causes a node to be rendered between its current position and it's last renderered position.

This should default to true for RigidBodies, and default to false otherwise.

In this design, you do not need to call set_interpolate_transform with a transform. The destination transform is always the current transform, and the other transform is always the last rendered transform.

This way, the same API is used for setting the transform on an interpolated or non interpolated node. Turning on and off interpolation is done completely by toggleing the is_interpolated flag.

A function could be added which disables interpolation for a single frame (reset_interpolation) which can be used for teleports.

To summarise my proposed API is this:

Spatial.is_interpolated(bool) Spatial.reset_interpolation()

(And similar for 2D)

jknightdoeswork commented 3 years ago

In our networking system, we also produce a different interpolation fraction, as we are not using physics_process to do our fixed update. Perhaps a Engine.set_interpolation_fraction(float) can be used to override this value. Additionally, if Engine.set_interpolation_fraction(-1) could restore it to use the automatic, physics_process based fraction.

This would be very customizable and extensible.

Calinou commented 3 years ago

In our networking system, we also produce a different interpolation fraction, as we are not using physics_process to do our fixed update.

See also https://github.com/godotengine/godot-proposals/issues/2020.

belzecue commented 3 years ago

Where does this leave _Engine.get_physics_interpolationfraction? Discovering that method and decoupling visual representation from physics tick solved my jitter problems.

https://github.com/belzecue/godot_various/blob/main/RigidBodyPhysicsTween.gd

jordo commented 3 years ago

There are a few things wrong in which I don't support in this proposal that I would hope can get addressed in the implementation of this proposal

* When working on multiplayer games, in order to send less information, games often run at a lower tick rate (say 10 or 15fps) in order to maximize bandwidth. 

A lower tick rate is purely related to reducing CPU utilization, not bandwidth. Tick rate is purely the rate in which the simulation moves from one state to the next. Networked games' send-rate or broadcast-rate, should be decoupled from the tick rate (it CAN be the same, but more often it's not). For example you can run a game server at a tick rate of 60hz, and a network broadcast rate of say 12hz. (This is very common as a higher tick rate helps minimize input latency).

Q: Will cubic/hermite/etc interpolation be supported? A: These interpolations are troublesome in many ways. Mostly because when rotating, velocity is not constant (objects can accelerate/decelerate), and trajectories can easily overshoot (go past, then go back). Additionally as an extra sample is needed, an extra physics frame of lag is added. So, in general this will most likely not be supported.

There are so many incorrect things with the above answer. Because this proposal is called 'physics step interpolation' we're talking about, interpolating values that are ticked in the physics step, and then rendered multiple times on the display's refresh rate (in-between each physics step).

A: These interpolations are troublesome in many ways. Mostly because when rotating, velocity is not constant (objects can accelerate/decelerate)

The above makes no sense in a physics tick context, and this statement is indicative you are probably viewing this problem from someone who has been working on vulkan rendering for the past year ;) (much appreciated btw). You are interpreting interpolation as purely 'geometric' and not rigid body motion. Yes, if you rotate geometry while moving (velocity), VERTICES will decelerate and accelerate, but this has absolutely NO meaning in rigid body motion or object positions. Objects will NOT accelerate and decelerate when rotating.

Additionally as an extra sample is needed, an extra physics frame of lag is added.

The above is 100% false. Given an object in TWO frames, below is the hermite cubic interpolation between frames. The four pieces of information is you need is:

And below is the formula to interpolate between these frames:

inline Vector2 hermite_interpolate(float t, Vector2 p1, Vector2 p2, Vector2 v1, Vector2 v2, float dt) {
    float t2 = powf(t, 2);
    float t3 = powf(t, 3);
    float a = 1 - 3*t2 + 2*t3;
    float b = t2 * (3 - 2*t);
    float c = dt * t * powf(t - 1, 2);
    float d = dt * t2 * (t - 1);
    return (a * p1) + (b * p2) + (c * v1) + (d * v2);
}

If you have velocity in your two tick states, why in the world would you NOT USE that data in your interpolant to get a closer approximation to the actual object path? I have used this in almost every physics based game I have built in the past 10 years, and it is verifiably better (based on delta error) than a linear lerp between positions that completely ignores the velocity component of an object.

You are basically opting to use this interpolant (ignoring velocity components completely):

Screen Shot 2021-05-24 at 8 58 05 PM

Over this:

Screen Shot 2021-05-24 at 8 57 53 PM

I can tell you with 100% certainty, if you are serious about supporting physics tick rates of 10-15hz, and actually rendering something that even looks remotely decent at 60-120hz rendering rates, you will be needing to use the better performing interpolant.

I don't think it's fair to users, and anyone potentially evaluating the engine to provide false reasons why something was chosen to be done in a certain way. If you want to try to implement interpolation in novel way that is different from other engines, (interpolate the raw geometry in the rendering server, and hopefully use it to boostrap TAA) then imo just say so. You will not have any velocity information at the geometry/rendering layer, which is why you will not be able to do any other type of interpolation other than positional based lerp.

If it's implemented as proposed, i just hope it's easy to turn off because we won't be using it. I can already say from experience that the interpolant that gives less delta error is always much better. Basically the difference between playable and unplayable at 10-15hz tick-rate in a game with any significant motion.

belzecue commented 3 years ago

Jordan, thanks for the code. For my physics_fraction script, I swapped out "interpolate_with" (and its origin linear interp.) for this hermite version and it works great.

# Given an object in TWO frames, below is the hermite cubic interpolation between frames.
# The four pieces of information is you need is:
# t = 0 to 1
# dt = delta time
# tickA position (p1)
# tickB position (p2)
# tickA velocity (v1)
# tickB velocity (v2)
func hermite_interpolate(t: float, p1: Vector3, p2: Vector3, v1: Vector3, v2: Vector3, dt: float) -> Vector3:
    var t2: float = pow(t, 2)
    var t3: float = pow(t, 3)
    var a: float = 1 - 3*t2 + 2*t3
    var b: float = t2 * (3 - 2*t)
    var c: float = dt * t * pow(t - 1, 2)
    var d: float = dt * t2 * (t - 1)
    return (a * p1) + (b * p2) + (c * v1) + (d * v2)
dedm0zaj commented 3 years ago

@nonunknown Unfortunately the plugin doesn't work for VR. The plugin uses function _process() for interpolation. For VR, using function _process() causes the controllers to move. https://github.com/GodotVR/godot_openvr/issues/124

@SaracenOne How do you use the plugin? Can you show me an example?

lawnjelly commented 3 years ago

@dedm0zaj I haven't used it myself for VR, but the principles inside the plugin are very simple, most of it is housekeeping. If you have a look at the source, you should be able to integrate them into your project.

Essentially it is just doing this:

render_position = old_pos + ((new_pos-old_pos) * Engine.get_physics_interpolation_fraction())

and similar for rotation / scale using either Basis lerping or Quat slerping.

Just beware that if you attempt to do this for a physics object as @jknightdoeswork does above, you risk creating a feedback loop between the interpolation and the physics .. i.e. setting the interpolated position changes the physics position. This is one of the reasons why the addon uses a separate node for this.

lawnjelly commented 2 years ago

As I'm currently having a go at implementing this (3D to start with) in two draft PRs (#52771 and #52846) I thought here would be as good a place as any to describe some practical problems. The interpolation itself has been pretty easy, providing the user interface to the system is where the difficulties lie.

Proposal PR #52771

https://github.com/godotengine/godot/pull/52771 I started off by making a PR which follows the proposal quite tightly. This adds a set_transform_interpolated and set_global_transform_interpolated function to the Spatial. Calling either of these propagates an interpolated flag to child nodes which is then used to pass this flag to the VisualServer when transforms are concatenated in children.

Physics nodes call set_global_transform_interpolated internally so always set off the interpolation pathway.

The main problems occur in Cameras, and any nodes that use any of the functions that internally call set_transform. These by nature will break the interpolation of that node (and children). There turn out to be quite a number of functions that call this internally:

    void set_translation(const Vector3 &p_translation);
    void set_rotation(const Vector3 &p_euler_rad);
    void set_rotation_degrees(const Vector3 &p_euler_deg);
    void set_scale(const Vector3 &p_scale);

    void rotate(const Vector3 &p_axis, float p_angle);
    void rotate_x(float p_angle);
    void rotate_y(float p_angle);
    void rotate_z(float p_angle);
    void translate(const Vector3 &p_offset);
    void scale(const Vector3 &p_ratio);

    void rotate_object_local(const Vector3 &p_axis, float p_angle);
    void scale_object_local(const Vector3 &p_scale);
    void translate_object_local(const Vector3 &p_offset);

    void global_rotate(const Vector3 &p_axis, float p_angle);
    void global_scale(const Vector3 &p_scale);
    void global_translate(const Vector3 &p_offset);

    void look_at(const Vector3 &p_target, const Vector3 &p_up);
    void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up);

    Vector3 to_local(Vector3 p_global) const;
    Vector3 to_global(Vector3 p_local) const;

    void orthonormalize();
    void set_identity();

Calling any of these in a repeated basis in _process or _physics_process will therefore break interpolation, if we follow the proposal exactly. This is particularly relevant in Cameras, but also Lights and any other nodes that wish to use these functions.

One of the suggestions was to add an interpolated bool argument to all these functions. This is possible (_although possibly not for the set_translate, set_rotate and set_scale calls as these are linked to the Transform property_) but doesn't seem ideal for something that we would normally want to set an interpolated flag on a node, then either always interpolate it, or never interpolate it, except in exceptional circumstances.

Alternative mechanism #52846

https://github.com/godotengine/godot/pull/52846 Given that all this is done to essentially determine a bool on each node, I also explored the obvious alternative option:

Instead of adding a new set of functions, we simply add a bool property to each node: physics_interpolated (or some such name). If a node is set to not interpolate, it never interpolates. If set to interpolate, it always interpolates, except in the special circumstance where the user calls a function teleport, which turns off interpolation for that physics tick.

This seems to me simple to use and makes converting existing games easy, in most cases you literally just turn on physics interpolation in the project settings, make sure you move things in _physics_process rather than _process, and you are done. And the implementation under the hood is straightforward.

This is working fine now.

Original option problems

So I am now currently trying to find a way to make the original option work in a practical sense, given the problems. There are some advantages to users being able to explicitly call an interpolated function, but there are a lot of special cases.

The good news is that the original option works for physics objects. The current suggestion is to make Cameras interpolate by default but all other objects only interpolate if set_transform_interpolated is called.

Two problems arise: 1) Cameras are not the only nodes that may want to use these other functions. You may for example want to make a Light follow an object with a look_at call. But doing this will break interpolation.

So we probably need some kind of way for all nodes to be able to force themselves to use interpolation, like a flag. Which brings us back to the alternative proposal...

An alternative would be to have a mechanism to say ignore previous calls and force interpolation this tick, kind of the opposite to a teleport call.

2) If Cameras do always interpolate by default, we then would need to add a function / mechanism to allow them to teleport. In addition the choice of whether to always interpolate the node would need to be switchable, because you may wish to interpolate a camera manually and process it in _process rather than _physics_process (e.g. InterpolatedCamera).

A possible solution is to have some logic involved in deciding whether or not a particular node interpolates: interpolated_inherited - flag set by calling set_transform or set_transform_interpolated, or inherited from parent interpolated_override - enum (inherited, on, or off)

This would be some kind of hybrid, where by default calling set_transform or set_transform_interpolated would decide whether interpolation was used, but this could be overridden per node.

Additional problem

reduz is not sure about having a teleport function, as he believes it could be confusing for users. So if we do have nodes that are by default interpolated, we need a way of switching this off for a tick if e.g. the user wants to move a player to a new location in the game, and not leave an interpolated trail but move there immediately.

Pros & Cons, fleshing out the proposal

I can see pros and cons with both approaches. At the moment I'm slightly more in the alternative approach camp, because I think turning things on and off like a light switch is easy to understand, it is tried and tested, simple internally, and I subscribe to the KISS principle. :grin: There are disadvantages though - for instance, there may be some situations internally in the engine we need to make a decision to call teleport. e.g. When objects are first loaded we probably want to teleport them rather than have them glide from 0,0,0 on the first frame..

The explicit approach has the opposite problem, we need to prevent seemingly innocuous calls from breaking the interpolation. Also as it stands, the proposal doesn't deal with Cameras and Lights etc, so needs fleshing out to describe how to handle these problem areas imo, as there is a big danger of the logic becoming overly convoluted.

Calinou commented 2 years ago

For reference, Godot 3.5beta has support for physics interpolation in 3D.

Future plans are to bring physics interpolation to 2D, then port it to Godot 4.x. Physics interpolation may be enabled by default in 4.x, but it will most likely be kept disabled by default in 3.x to avoid breaking compatibility with existing projects.

iamjoeysox commented 1 year ago

What is the status of this on 4.0? Im kinda stuck on starting a major project wondering if this will be ready soon or if I should still stick with 3.5x? Thanks again!

Calinou commented 1 year ago

What is the status of this on 4.0? Im kinda stuck on starting a major project wondering if this will be ready soon or if I should still stick with 3.5x? Thanks again!

The physics step interpolation in 3.5 won't be ported for 4.0 in time; it will be done in a future 4.x release instead. The exact implementation will likely differ though, as there are multiple ways to perform physics interpolation in Godot (scene-side vs server-side).

Also, 2D physics interpolation may be implemented in 3.6 first to ensure it doesn't have to be designed and implemented twice (once in 3.x, once in 4.x).

In any case, lawnjelly's smoothing-addon has been ported for 4.0: https://github.com/lawnjelly/smoothing-addon/tree/4.x

iamjoeysox commented 1 year ago

What is the status of this on 4.0? Im kinda stuck on starting a major project wondering if this will be ready soon or if I should still stick with 3.5x? Thanks again!

The physics step interpolation in 3.5 won't be ported for 4.0 in time; it will be done in a future 4.x release instead. The exact implementation will likely differ though, as there are multiple ways to perform physics interpolation in Godot (scene-side vs server-side).

Also, 2D physics interpolation may be implemented in 3.6 first to ensure it doesn't have to be designed and implemented twice (once in 3.x, once in 4.x).

In any case, lawnjelly's smoothing-addon has been ported for 4.0: https://github.com/lawnjelly/smoothing-addon/tree/4.x

Okay, thank you for the detailed information, this was super helpful. Do you by any chance know if this a high priority for the team? Or does the lawn jelly add-on offer a good enough/stable solution for now for commercial releases? Thanks again for all your quick and helpful responses, feels kinda odd to actually get responses let alone helpful ones from bigger projects haha. You all are awesome!

belzecue commented 1 year ago

Apart from lawnjelly's addon, you can easily roll your own for 4.0 if you prefer to understand what's going on under the hood. It's better to have the logic baked in at the engine level versus script, of course, but the result is the same: smooth movement at any physics tick. The concept is pretty easy to understand once you think about it as separating your physics (simulation) and visuals (presentation) instead of locking visuals to physics. There's a link to an example 3.x project (and explanation) in my Reddit post here. I expect not much changes doing it in 4.0:

https://www.reddit.com/r/godot/comments/rbqz94/butterysmooth_rigidbody_movement_at_any_physics/

iamjoeysox commented 1 year ago

Okay cool, I am pretty familiar with the fix and how its accomplished, I guess I was just hoping for a stable way to do it while being able to use CharacterBody2D, etc as the root/parent node for various reasons.

Have you seen any issues with allowing the follow target to be the parent in 4.0? I know it was "experimental" in 3.5, but wasn't sure if more testing was done in 4.0 with that particular issue and I did a bunch of my own testing quite a few months back before the official addition of the lawn jelly commits and things seemed a little hacky/inconsistent with managing global in/out etc, but if others are using this method in production then I would feel more comfortable with that fix for now.

lawnjelly commented 1 year ago

Have you seen any issues with allowing the follow target to be the parent in 4.0?

You may be able to do it sensibly in combination with a call to set_as_toplevel(). I wasn't aware of this functionality when I originally wrote the addon. This should hopefully bypass the feedback effect that can throw everything off.

I actually added this to the doc a while ago, although it may be available for canvas_item in addition to spatial (although I haven't tested this). You would make the smoothing node a child of the parent and call set_as_toplevel() in the _ready() or something like that.

bobmoff commented 1 year ago

+1 for set_as_toplevel

That is how I have implemented it in my projects.

I modified the your plugin by adding it to the ready function. Just like you described.

lawnjelly commented 1 year ago

I modified the your plugin by adding it to the ready function. Just like you described.

That's great. We should probably have a look at putting this in the addon in the _ready(), rather than just a mention in the docs which is currently the case (if me or @Calinou get a moment!). :+1:

iamjoeysox commented 1 year ago

Have you seen any issues with allowing the follow target to be the parent in 4.0?

You may be able to do it sensibly in combination with a call to set_as_toplevel(). I wasn't aware of this functionality when I originally wrote the addon. This should hopefully bypass the feedback effect that can throw everything off.

I actually added this to the doc a while ago, although it may be available for canvas_item in addition to spatial (although I haven't tested this). You would make the smoothing node a child of the parent and call set_as_toplevel() in the _ready() or something like that.

Awesome! Thanks a ton for stepping in and helping us figure this out/clarify. I believe that's about how I was doing it months ago in 3.5, so I'm glad a similar approach should work in 4.0. This particular issue threw a gear into our well thought out framework we've built on top of Godot which relied on using CharacterBody2D, etc as top-level nodes within scenes.

thygrrr commented 11 months ago

I solved this with a critical spring damper node that works exactly like RemoteTransform3D otherwise.

I wanted to make a proposal to extend RemoteTransform3D to do this. But a renderer based solution is likely preferable.

Writing your own critical spring is a fairly advanced approach, but I concur with the reports that the physics fraction value from the engine isn't accurate. So it was needed for my project. (for me to be satisfied with the smoothness of motion)

My suspicion is that whatever jitter_fix does is it introduces error into the time step and fraction.

The game physics judder a lot if vsync is on, even at 120 Hz refresh. It's better if vsync is off, and I use the spring damper (aka "Smoothdamp")

(This is particularly true for CharacterBody3D or AnimatableBody3D, I'm not talking about the usual physics jitter)

lawnjelly commented 11 months ago

My suspicion is that whatever jitter_fix does is it introduces error into the time step and fraction.

Jitter fix should be set to 0.0 when doing anything involving physics interpolation or interpolation fraction, it will muck up all the timing. The core physics interpolation and the addon do this automatically. This could possibly be added to the docs for get_physics_interpolation_fraction() particularly in master.

It turns out this is already mentioned in the docs for 3.6 for physics_jitter_fix: https://docs.godotengine.org/en/3.6/classes/class_engine.html#property-descriptions

Note: For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting physics_jitter_fix to 0.

(Jitter fix is not really needed in most cases now there is delta smoothing, but it was preserved for backward compatibility. I've tried to add the option to bypass it in the past but PR was never merged.)

I concur with the reports that the physics fraction value from the engine isn't accurate.

Please link reports / issue.

Lippanon commented 10 months ago

Has any work begun on this for 4.x? Is there a timeframe? Does reduz want to do it himself but hasn't found the time? The 3.x interpolation works very well and is simple to use. Having to rely on an addon that runs on GDScript and requires extra nodes for every physics body is not ideal (or of course write your own engine modification...).

lawnjelly commented 10 months ago

I'm kind of waiting on testing from 3.6 which has 2D physics interpolation, as I was thinking in terms of doing 2D interpolation first in 4.x. We haven't had a beta for a while which has a set of fixes for 2D interpolation (due to priorities being on 4.x) so that's one of the sticking points.

Also, if you want physics interpolation in 4.x, be sure to download 3.6 betas and give it a try and leave feedback on the system in 3.x (positive, negative, even that you tried it and it worked fine etc). The more testing it gets, the better the implementation in 4.x, and the quicker it makes it into 4.x.

The reason for currently thinking of 2D first is:

Most problematic in 4.x is likely to be particles, especially GPUParticles if CPU is no longer supported.

mruncreative commented 4 months ago

Please add it to 4.x already. It already worked beautifully in 3.5. Maybe not in all cases but the basics worked very well and super smooth on high refresh rate screens and manually interpolating in gdscript adds so much spaghetti code. Something would be better than nothing, even if it isn't perfect right away. You could have it opt-in for now like with 3.x.

Uxeron commented 4 months ago

Physics interpolation for 2D was actually merged into master a month ago (88424), and should show up in the next 4.3 dev snapshot.

TrieBr commented 4 months ago

I just tried this in 4.3dev6 and it works fantastically well (at least for my 2d platformer) with zero changes required other than enabling the setting. Kudos to rburing, lawnjelly and other contributors!

Edit: I don't know if this is a bug in this feature of ParallaxLayer, but it seems like ParallaxLayer is tied to the physics step, and interpolation is not working on them so the backgrounds are extremely choppy with a low physics rate (mine is 15hz).

lawnjelly commented 4 months ago

Edit: I don't know if this is a bug in this feature of ParallaxLayer, but it seems like ParallaxLayer is tied to the physics step, and interpolation is not working on them so the backgrounds are extremely choppy with a low physics rate (mine is 15hz).

This might be better to report in an issue. I had to tweak things to get parallax working in 3.x (defaulting interpolation to off for these nodes), but there is a new parallax node in 4.x I gather so @rburing will probably investigate.

rburing commented 4 months ago

Indeed, @TrieBr please report this as an issue with a (minimal) reproduction project attached so we can investigate. The default interpolation mode should already be off, so I'm not immediately sure what the problem is.

TrieBr commented 4 months ago

While making a minimal repro, I found the issue. In my case my Parallax2D was a child of the Camera2D. Once I moved them as a sibling to the camera the stutter went away. Thanks again and appreciate your responsiveness!

Edit: I stand corrected. It seems the bug is in Parallax2D. but ParallaxBackground / ParallaxLayer work as expected. I'll file a bug for Parallax2D. Filed issue: https://github.com/godotengine/godot/issues/91625

dreadpon commented 3 months ago

So glad this is finally getting implemented, thank you @rburing! Is there any chance an extrapolation algorithm is planned somewhere in the future? I currently use lawnjelly's Smoothing plugin, and when things move above 10-20 u/s (especially players), the interpolation lags behind quite a bit at 30 physics ticks per second (understandably so). I had to add some custom simple extrapolation based on linear velocity to account for such scenarios.

I realize compared to interpolation, extrapolation might contain too much "guesswork" to be included in core, as well as not be relevant in most scenarios, but maybe at least some kind of endpoint to support user-defined extrapolation would be possible?

Calinou commented 3 months ago

Is there any chance an extrapolation algorithm is planned somewhere in the future?

For networking use cases, you'll need to program your own interpolation (as relying on the built-in physics interpolation won't suffice). This is because network interpolation needs some level of buffering to be stable when packet loss occurs, which necessarily adds latency. Your network tickrate may also be different from the physics update rate, particularly when you have client-side prediction going on. See https://github.com/godotengine/godot-proposals/issues/7280.

Extrapolation can help reduce the perceived latency, but it will also reduce stability when unpredictable velocity changes occur (e.g. when a player dashes in various directions). Personally, I would only use it in a racing game when you need other cars to be on the exact same time as you (like Trackmania in rounds mode). It's very uncommon for games like FPSes to use extrapolation.