godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.19k stars 21.21k forks source link

[Bullet] Bullet Physics HingeJoint motor stops and restarts spinning every 360° #18351

Open Amil-francescolima74 opened 6 years ago

Amil-francescolima74 commented 6 years ago

Godot version: Godot 3.0.2 official

OS/device including version: macOS Sierra 10.12.6

Issue description: Using HingeJoint and activating motor it stops and restarts spinning every 360°. This happens only using default physics engine settings (Bullet). Switching to Godot physic engine the motor spins regurarly.

Steps to reproduce: Create a simple 3D scene and add a camera. Add a StaticBody and a RigidBody with its own MeshInstance and CollisionShape using a cube as mesh. Add a HingeJoint and in its nodes properties set the StaticBody (Node A) and the RigidBody (Node B). In the HingeJoint properties enable the motor and run the project.

I made also a video, see the second half. https://youtu.be/htBEKQu8Iy4

Minimal reproduction project:

HingeJoint_issue.zip

lmjk commented 6 years ago

On my machine (Fedora, 64bit) the cube does not resume spinning at all after the second time it stops. If I set motor/max_impulse as low as 0.06, the cube does not have full stops anymore, it only slows down temporarily instead.

akien-mga commented 6 years ago

CC @AndreaCatania

AndreaCatania commented 6 years ago

Thanks you for the issue report, I will let you know soon

AndreaCatania commented 6 years ago

Can you please try to lower the max impulse?

It seems correctly implemented, I should check it with Erwin.

Skwint commented 6 years ago

I have this problem. Master branch, windows 7, 64 bit. I am having to lower my max impulse to 0.01 to prevent it happening - 0.03 and it pauses for half a second every time it reaches 180 degrees from the starting point. I can cheerfully provide a Godot project that demonstrates it if anyone needs. The default impulse is 1.0 on HingeJoints.

edit: Increasing the mass or target velocity also helps overcome the hiccup. I'd guess having a momentum significantly higher than the maximum impulse causes smaller problems. It is not going to sleep, although in the cases where it gets stuck forever I suspect it might be eventually going to sleep as a result of the bug, not the cause of it.

Skwint commented 6 years ago

OK, I've tracked it down. In btHingeConstraint::getInfo2InternalUsingFrameOffset there is a call to btTypedConstraint::getMotorFactor which passes in (and then uses) the angular limits of the constraint in calculating the force the motor will apply even if the limits are disabled. I consider this a bug in Bullet. We can work around it in Godot, however (well, for non extreme cases) by this. It's a one line change that means that the limits are pretty much never actually reached and so don't cause problems. I'm trying to attract the attention of some Bullet dudes to talk over their bit and see if it needs patching properly:

index 97ea7ca3d..cc8f18767 100644
--- a/modules/bullet/hinge_joint_bullet.cpp
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -155,7 +155,7 @@ void HingeJointBullet::set_flag(PhysicsServer::HingeJointFlag p_flag, bool
p_val
        switch (p_flag) {
                case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT:
                        if (!p_value) {
-                               hingeConstraint->setLimit(-Math_PI, Math_PI);
+                               hingeConstraint->setLimit(-1.5f * Math_PI, 1.5f * Math_PI);
                        }
                        break;
                case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR:

EDIT: I realize this may sound like I'm fixing it. I'm not. I'm just explaining how it can be!

AndreaCatania commented 6 years ago

Erwin said me couple of times to not use Hinge joint since its implementation is really old, he told me to use btHinge2Constraint. It's a bit different compared to godot hinge and for this reason I didn't implemented it yet.

However I will change it soon, so this bug will be fixed

MolbOrg commented 6 years ago

3.1 still a problem

wombatstampede commented 5 years ago

Confirming that this bug still occurs in 3.1beta3. (Bullet physics engine only)

The workaround (IMHO) is to use the Angular Motor of Generic6DOFJoint.

theblacktiger59 commented 4 years ago

Wondering if there is any update on this issue ? (as it has been removed from the 3.2 milestone) More specifically, is the Hinge joint in godot 3.2 still the old implementation ? (It does stop every turn still. And the Generic 6DOF Joint suffer a y axis gimbal lock.)

pwab commented 4 years ago

More specifically, is the Hinge joint in godot 3.2 still the old implementation ?

As far as I can see nothing has changed since then. Went into this issue yesterday with current 3.2.1.stable.

Maybe this was dropped from 3.2 milestone because of the release in January and then it was forgotten to readd it to 4.0. Any chance this is getting on the list again @AndreaCatania?

Zotyx commented 4 years ago

Hey @AndreaCatania, this problem still occrurs in v3.2.3. When will it be fixed please ?

AndreaCatania commented 4 years ago

Hello, sorry for the delay I been busy this period and I can't tell an exact date when this will be fixed. Tho, the 6dof joint has a much better algorithm, you could consider using it in the meanwhile.

RedEagle09 commented 3 years ago

3.2.3, still not fixed >:(

Calinou commented 3 years ago

3.2.3, still not fixed >:(

Can you reproduce this issue after upgrading to 3.3.2? The Godot 3.2.x series is not supported anymore.

RedEagle09 commented 3 years ago

3.2.3, still not fixed >:(

Can you reproduce this issue after upgrading to 3.3.2? The Godot 3.2.x series is not supported anymore.

will try soon. will need to download it, as well as the export files...

pwab commented 3 years ago

@Calinou I just opened the minimal reproduction project made by @Amil-francescolima74 in the web editor and the issue is still reproducible.

RedEagle09 commented 3 years ago

@Calinou I just opened the minimal reproduction project made by @Amil-francescolima74 in the web editor and the issue is still reproducible.

how to workaround then?

pwab commented 3 years ago

how to workaround then?

Well you could do the following:

  1. Deactivate the motor
  2. Add a constant angular_velocity
  3. Change the angular_damping to 0
  4. Add a script to give your body a constant push.

Now you have different possibilities:

Problem with the first two is, that if you want a (physically incorrect) motor that spins with a constant velocity even when under load you need to constantly check the angular_velocity and rise or lower the torque/impulse (you might need PID control then). With changing the state directly you can force the body to do what you want (haven't worked with states so this might be not correct).


I wanted to add a simple workaround example but I ran into #42842 so I really had to add a control sequence watching the velocity. I'm still not sure why the torque isn't applied constantly though (without readding torque the body slows down) 😕:

extends RigidBody

const torque = Vector3(0,0,1)

func _process(delta):
    if angular_velocity.z < 1:
        add_torque(torque)

I think it could be better to manipulate the velocity directly in the BodyState.

Give my solution a try if you wish: hingejoint_issue_workaround.zip

RedEagle09 commented 3 years ago

how to workaround then?

Well you could do the following:

1. Deactivate the motor

2. Add a constant `angular_velocity`

3. Change the `angular_damping` to 0

4. Add a script to give your body a constant push.

Now you have different possibilities:

* Use `add_torque` in the `_ready` function

* Use `apply_impulse` in the `_process` function

* Use the [PhysicsDirectBodyState](https://docs.godotengine.org/en/stable/classes/class_physicsdirectbodystate.html) to manipulate the velocity to your liking

Problem with the first two is, that if you want a (physically incorrect) motor that spins with a constant velocity even when under load you need to constantly check the angular_velocity and rise or lower the torque/impulse (you might need PID control then). With changing the state directly you can force the body to do what you want (haven't worked with states so this might be not correct).

I wanted to add a simple workaround example but I ran into #42842 so I really had to add a control sequence watching the velocity. I'm still not sure why the torque isn't applied constantly though (without readding torque the body slows down) 😕:

extends RigidBody

const torque = Vector3(0,0,1)

func _process(delta):
  if angular_velocity.z < 1:
      add_torque(torque)

I think it could be better to manipulate the velocity directly in the BodyState.

Give my solution a try if you wish: hingejoint_issue_workaround.zip

will try that, except i think motor is in local coordinates, and angular velocity is in global, right? i dunno how to translate rotation from global to local space...

pwab commented 3 years ago

will try that, except i think motor is in local coordinates, and angular velocity is in global, right? i dunno how to translate rotation from global to local space...

I think not. I'm pretty sure that the angular_velocity is a parameter based on the bodys local coordinate system.

But anyway: I found a better workaround! As @AndreaCatania said you could use the Generic6DOFJoint. It also has the motor ability and the issue isn't present there.

Here is the reworked workaround (the one on the right is the 6DOFJoint): hingejoint_issue_workaround.zip

RedEagle09 commented 3 years ago

will try that, except i think motor is in local coordinates, and angular velocity is in global, right? i dunno how to translate rotation from global to local space...

I think not. I'm pretty sure that the angular_velocity is a parameter based on the bodys local coordinate system.

But anyway: I found a better workaround! As @AndreaCatania said you could use the Generic6DOFJoint. It also has the motor ability and the issue isn't present there.

Here is the reworked workaround (the one on the right is the 6DOFJoint): hingejoint_issue_workaround.zip

will try later. ty!