SpringBoneSimulator3D Behaviour is tied to Framerate
Tested versions
Godot v4.4.rc1
System information
Windows 10 (build 19045) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3090 (NVIDIA; 32.0.15.6614) - AMD Ryzen 9 5900XT 16-Core Processor (32 threads)
Issue description
The new Spring bones are not frame independent, lower frames increase movement.
Unlocked framerate and 165 FPS https://youtu.be/EhmwdM0wvsk
60 FPS https://youtu.be/I_-KNPUBdtI
as you can see in the videos, I put spring bones on the characters ears, and as I lower the framerate the ears bounce more and more.
Steps to reproduce
Change the monitor's refresh rate while using Spring bones
Use arrow keys to move the cylinder on test scene
Minimal reproduction project (MRP)
The default callback modifier seems to be Idle which results in using the process delta not the physics delta. Does changing this setting to Physics on the skeleton solve the problem?
The default callback modifier seems to be
Idlewhich results in using the process delta not the physics delta. Does changing this setting toPhysicson the skeleton solve the problem?
no, it made the bone behave different but there are still diferences with framerate unlocked and locked with vsync
Callback modifier is set to physics in this video.
https://youtu.be/SVunDt78P0A
its basically the same, movement increases as framerate goes down
I'll see if I can find a better way to interpolate when I have time, but I suspect it is mostly a SpringBone specification (so if it can be solved, it will be provided as an option).
First SpringBone translates joint and then converts it to a rotation by pushing out in the normal direction from center.
As you can see in the figure above, at this point, the smaller the frame rate, the larger the velocity.
SpringBone's algorithm calculates the next coordinate by integrating the velocity, so this difference affects the result. I marginally expect that the difference possibly be reduced by some approaches:
- Multiplying by some of the coefficients such as dependent on the pushing out distance, like some kind of averaging
- Predicts the next coordinate of the joint when there is no length limit based on velocity and calculates the pushing out point by including the predicted point as a factor in the calculation
- Pushing out a joint to a vector that is not from center. For example, create a plane ABC passing through three points: point A before the move, current point B, and center point C. In plane ABC, create a vector orthogonal to line AB and going from point B to the face of the sphere. two vectors are possible, the shorter of which is adopted. For example, in the figure above, we can consider a vector from t1 to up
Also, the other workaround I can think of is to simulate a lower frame rate in the _process() or _physics_process(), but I think we need to discuss whether it is good for Godot as a whole to manage FPS on a per-node basis. Perhaps the concept needs to be common for things like LOD of animation FPS.
Also worth a mention, physics interpolation doesn't affect SpringBoneSimulator3D i believe. Result of SpringBoneSimulator3D becomes jittery when used with physics interpolation enabled, or at least the result is not interpolated so the jittering becomes apparent.
Also worth a mention, physics interpolation doesn't affect SpringBoneSimulator3D i believe. Result of SpringBoneSimulator3D becomes jittery when used with physics interpolation enabled, or at least the result is not interpolated so the jittering becomes apparent.
See https://github.com/godotengine/godot-proposals/issues/10770.
I tested the MRP in 4.5 and latest master (0400b70).
The issue is still present when the skeleton's callback mode is Idle. Setting the skeleton's callback mode to Physics fixes the issue for me, although the lack of physics interpolation is noticeable.
I feel like the best way to fix this issue would be to resolve the lack of physics interpolation. It might also be nice to be able to set the callback mode on each modifier rather than only on the whole skeleton.
@apples Physical interpolation for node movement has been definitely added in https://github.com/godotengine/godot/pull/110987.
However, physical interpolation is not applied to the internal joint movement itself by SpringBone but since we are not using PhysicalServer for bone movement and it relies on integration math, it is unclear whether this can adopt physical interpolation.
Also if applying physical interpolation to SpringBone bones, I assume it should be implemented within the pose calculation in the Skeleton3D class and retrieved by a function like get_bone_pose_interpolated().
A concern is that parent bone movements may occur within the same frame as retrieving get_bone_pose_interpolated(). Therefore, it is unclear whether implementing this is technically feasible.
@lawnjelly Do you have any ideas about incorporating something like physical interpolation into Skeleton3D's pose calculation?
I tested the MRP in 4.5 and latest master (
0400b70).The issue is still present when the skeleton's callback mode is Idle. Setting the skeleton's callback mode to Physics fixes the issue for me, although the lack of physics interpolation is noticeable.
I feel like the best way to fix this issue would be to resolve the lack of physics interpolation. It might also be nice to be able to set the callback mode on each modifier rather than only on the whole skeleton.
yeah setting it to physics makes it consistent but the lack of interpolation is really noticable if you have a high refresh rate monitor or if your physics ticks per second are set to something like 30