Godot: 3d physics stepping is two frames late for bullet physics

Created on 8 Apr 2020  路  8Comments  路  Source: godotengine/godot

Godot version:

3.2.1-stable

OS/device including version:

Mac

Issue description:

3d physics stepping is two frames late using bullet physics. Expected would be that it is one frame late.

Steps to reproduce:

Apply either velocity or force to a 3d rigid body. You will notice that the object moves two frames after you apply the velocity.

Minimal reproduction project:

Create a 3d scene and add rigid body with this script.

extends RigidBody

var frame = 0

func _ready():
    pass # Replace with function body.

func _physics_process(delta):
    match frame:
        10:
            linear_velocity = Vector3(1,0,0)
        12:
            linear_velocity = Vector3(0,0,0)

    force_update_transform()
    print("frame: " + str(frame) + " vel " + str(linear_velocity) +" " + str(transform.origin))

    frame += 1

The output is

frame: 9 vel (0, 0, 0) (0, 0, 0)
frame: 10 vel (1, 0, 0) (0, 0, 0)
frame: 11 vel (1, 0, 0) (0, 0, 0)
frame: 12 vel (0, 0, 0) (0.1, 0, 0)
frame: 13 vel (0, 0, 0) (0.2, 0, 0)
frame: 14 vel (0, 0, 0) (0.2, 0, 0)

Expected would be that body has moved at frame 11.

* Note *

Using godot physics is the update is only one frame late. Hence bullet physics works differently.

Create equivalent script for 2d. You will notice that the body moves on frame 11. Hence, 2d physics works correctly.

bug physics

Most helpful comment

I tried to figure this out on my own. Thankfully the source code of Godot is very well structured and easy to read - big thanks for that.

Anyway, the situation looks like this to me:

#
Bullet library does not return the most resent position until the pre-tick in the next frame. This sound a bit weird to me but after a lot of log outputs, this is what it looks like.
#

  • Currently the update for bullet looks likes
  • _physics_process
  • bullet::stepSimulation
    2.1 pre-tick
    foreach object
    apply xform from prev frame
    _integrate_forces
    2.2. tick (most recent xform not available)
  • _process

One might think that a work around would be be to never use _physics_process and only rely on _integrate_forces. However, this would conflict with e.g. the 2D physics works. Also, the _integrate_forces is called right after the xform is applied. We wouldn't be able to trust that other objects in the scene have yet had their xforms synced.

  • Conclusion

I would suggest that we change so that _physics_process is called from insde the pre-tick. And split the RigidBody::_direct_state_changed functions so that transforms are applied before any process or integrate functions. Like this:

  1. bullet::stepSimulation
    1.1 pre-tick
    foreach object
    apply xform from prev frame
    foreach object
    _physics_process
    foreach object
    _integrate_forces
    1.2. tick (most recent xform not available)
  2. _process

Maybe this setup would make it more stable? What do you guys think?

It's not perfect because the rendering will always use transforms that are one frame late. But in case we can't read the most recent transforms from bullet there is not much we can do. At least it is not two frames late.

All 8 comments

cc @AndreaCatania

@huhund Does this occur if you use _integrate_forces(state) instead of _physics_process(delta)? This is generally recommended to decrease RigidBody latency by one frame.

Thanks Calinou for looking into this.

  • No, _integrate_forces doesn't apply velocity correctly. Same issue persist also if apply impulse instead of setting velocity.

    • The only case that works correctly is in 2d-physics using _physics_process.
    • So a new bug actually surfaced: using 2d and _integrate_forces the physics is one frame late
  • Also, update order is different between Bullet 3D and Godot 2D physics

bullet 3d is

  • _physics_process
  • _integrate_forces
  • _process
  • ( _process ... )

while godot 2d is

  • _physics_process
  • _process
  • ( _process ... )
  • _integrate_forces

I added a test project to github: https://github.com/huhund/godot_tests.git
Where the 4 uses cases are implemented


  • Godot 2D single threaded physics, setting velocity in _physics_process

    • position is applied as expected :)

  • Godot 2D single threaded physics, setting velocity in _integrate_forces

    • position is applied one frame late :(

  • Bullet 3D, setting velocity in _physics process

    • position is applied one frame late :(

  • Bullet 3D, setting velocity in _integrate_forces

    • position is applied one frame late :(

I hope this info is helping. Thanks for a great engine.

I tried to figure this out on my own. Thankfully the source code of Godot is very well structured and easy to read - big thanks for that.

Anyway, the situation looks like this to me:

#
Bullet library does not return the most resent position until the pre-tick in the next frame. This sound a bit weird to me but after a lot of log outputs, this is what it looks like.
#

  • Currently the update for bullet looks likes
  • _physics_process
  • bullet::stepSimulation
    2.1 pre-tick
    foreach object
    apply xform from prev frame
    _integrate_forces
    2.2. tick (most recent xform not available)
  • _process

One might think that a work around would be be to never use _physics_process and only rely on _integrate_forces. However, this would conflict with e.g. the 2D physics works. Also, the _integrate_forces is called right after the xform is applied. We wouldn't be able to trust that other objects in the scene have yet had their xforms synced.

  • Conclusion

I would suggest that we change so that _physics_process is called from insde the pre-tick. And split the RigidBody::_direct_state_changed functions so that transforms are applied before any process or integrate functions. Like this:

  1. bullet::stepSimulation
    1.1 pre-tick
    foreach object
    apply xform from prev frame
    foreach object
    _physics_process
    foreach object
    _integrate_forces
    1.2. tick (most recent xform not available)
  2. _process

Maybe this setup would make it more stable? What do you guys think?

It's not perfect because the rendering will always use transforms that are one frame late. But in case we can't read the most recent transforms from bullet there is not much we can do. At least it is not two frames late.

Big thanks for looking into this.

However, can we keep this bug report open? I can still see the two frame issue for Bullet 3D physics in 3.2.3.RC3.

I think the issue is that Bullet library does not return the most resent position until the Bullet pre-tick callback in the next frame.

Currently I can work around the issue just by using Godot 3D physics.

Probably this is not yet back ported?

The 3.2.3.RC3 release log says it should have the #40184 commit. I'm happy to test again later if I had the wrong version.

My understanding is that issues should be closed if they're fixed in master.

40184 was backported (#40185) but then reverted (with 10544f1cf777), because of regression issues. It will be reapplied with #40788, but it depends on whether #40486 is considered a bug or not.

The 3.2.3.RC3 release log says it should have the #40184 commit.

The release log needs to be updated to reflect the reversion.

Was this page helpful?
0 / 5 - 0 ratings