Open Wc4ever opened 2 weeks ago
Hello,
This is a known issue and has been discussed before, see: https://github.com/godot-jolt/godot-jolt/issues/374#issuecomment-1555939027
It is something I should look into though (at least confirm that other physics engines don't have this behavior / don't do the opposite of slowly losing energy).
Hi, thanks for answer. I could post info of how it works in other engines if you wish.
Here is test from Bullet. Every damping and friction set to 0. Looks like bullet calculates restitution as multiplication so both objects has to be 1.0 otherwise it dont bounce at all.
https://github.com/user-attachments/assets/a77816e9-bf8e-4a81-b4f2-ea640982fd31
Open Dynamics Engine surface bounce 1.0
https://github.com/user-attachments/assets/7bfe22de-275a-4f6c-a946-d5a2a9b35db9
Unity build-in engine Gosh looks like it grows in Unity also(unexpected). Material settings Bounciness 1.0 Combine type Maximum.
https://github.com/user-attachments/assets/0c643329-8a24-4ede-a158-98fa86779385
Flax Engine (Physx 5) Wierdest one so far. Set both material Combine type Maximum and restitution 1.0 and it grows every bounce almost the same as in Jolt. But if I set Combine type Multiply and all materials to 1.0 seems it slows down but eventualy grows up again and then slows down.
https://github.com/user-attachments/assets/e56fa9a3-c09d-48fd-93ef-17a5c9f8ff70
So seems I was wrong about "other engines" but at the same time rebouns at the same height makes more sense to me.
At this point if you belive there is no bug I think it could be just declarated as expected behaviour and closed.
I'll leave the bug open for now. It's definitively not physically correct. I'll see if there's a way to make this better.
Mainly for future me, I've simplified the simulation loop to:
// Code to simulate a sphere of radius 2 falling from 10 units high
Vec3 pos = Vec3(0, 10, 0);
Vec3 vel = Vec3::sZero();
Vec3 g(0, -9.81f, 0);
float dt = 1.0f / 60.0f;
float maxy = 0;
for (int i = 0; i < 1000; ++i)
{
// Apply gravity
// Equivalent to PhysicsSystem::JobApplyGravity
vel += g * dt;
// If we're penetrating the ground, we have to reverse the velocity (corresponds to restitution 1).
// Equivalent to PhysicsSystem::JobSolveVelocityConstraints
float penetration = 2.0f - pos.GetY();
if (penetration > 0 && vel.GetY() < 0.0f)
vel = -vel;
// Update position
// Equivalent to PhysicsSystem::JobIntegrateVelocity
pos += vel * dt;
// Record new height records (note we only trace when we're going down again to avoid tracing multiple lines for the same bounce)
if (vel.GetY() <= 0.0f && pos.GetY() > maxy)
{
maxy = pos.GetY();
Trace("Simulated new high: %f", maxy);
}
}
which produces:
Simulated new high: 9.997275
Simulated new high: 10.209825
Simulated new high: 10.425099
Simulated new high: 10.643100
Simulated new high: 10.863826
Simulated new high: 11.087276
Simulated new high: 11.313452
When simulating the same setup in Jolt we get the exact same results:
New high point: 10.000000
New high point: 10.209825
New high point: 10.425100
New high point: 10.643101
New high point: 10.863826
New high point: 11.087276
New high point: 11.313451
Implementation: SimpleTest.zip
As said in the other ticket: When the collision has been detected, the gravity has been applied already so the bounce velocity is overestimated.
https://github.com/user-attachments/assets/7b9c515f-9490-4a32-9d19-ebed71b9a34b
Could this be related to experiencing infinite bounce? This is with friction/restitution 0.8/0.8 on the ball and 1.0/0.9 on the plane.
If I adjust the code above with your parameters:
// Code to simulate a sphere of radius 2 falling from 10 units high
Vec3 pos = Vec3(0, 10, 0);
Vec3 vel = Vec3::sZero();
Vec3 g(0, -9.81f, 0);
float dt = 1.0f / 60.0f;
float restitution_floor = 0.9f;
float restitution_sphere = 0.8f;
float restitution_combined = max(restitution_floor, restitution_sphere);
for (int i = 0; i < 1000; ++i)
{
bool positive_before = vel.GetY() >= 0.0f;
// Apply gravity
// Equivalent to PhysicsSystem::JobApplyGravity
vel += g * dt;
// If we're penetrating the ground, we apply restitution
// Equivalent to PhysicsSystem::JobSolveVelocityConstraints
float penetration = 2.0f - pos.GetY();
if (penetration > 0 && vel.GetY() < 0.0f)
vel = -restitution_combined * vel;
// If velocity changes from positive to negative, we trace the height
bool positive_after = vel.GetY() >= 0.0f;
if (positive_before && !positive_after)
Trace("Bounce height: %f", pos.GetY());
// Update position
// Equivalent to PhysicsSystem::JobIntegrateVelocity
pos += vel * dt;
}
Then no:
Bounce height: 10.000000
Bounce height: 8.627144
Bounce height: 7.512838
Bounce height: 6.609490
Bounce height: 5.857011
Bounce height: 5.247907
Bounce height: 4.739192
Bounce height: 4.324055
Bounce height: 3.972433
Bounce height: 3.679149
But as you can see from the code above, it depends on more than just the restitution (gravity and delta time).
Thank you for your reply @jrouwe :) Appreciated.
I took your code and changed the iterations to 4000 and I see the same effect, I think? I can provide the physics world code I am using for the above video too. I may well be doing something daft tho'!
Edit: FWIW, I think this pretty much sorts it for both situations. I'm not yet familiar enough with your code to suggest a patch tho'!
// Code to simulate a sphere of radius 2 falling from 10 units high
Vec3 pos = Vec3(0, 10, 0);
Vec3 vel = Vec3::sZero();
Vec3 g(0, -9.81f, 0);
float dt = 1.0f / 60.0f;
float restitution_floor = 0.9f;
float restitution_sphere = 0.8f;
float restitution_combined = max(restitution_floor, restitution_sphere);
for (int i = 0; i < 10000; ++i)
{
bool positive_before = vel.GetY() >= 0.0f;
// Apply gravity
// Equivalent to PhysicsSystem::JobApplyGravity
vel += g * dt;
// If we're penetrating the ground, we have to reverse the velocity (corresponds to restitution 1).
// Equivalent to PhysicsSystem::JobSolveVelocityConstraints
float penetration = 2.0f - pos.GetY();
if (penetration > 0 && vel.GetY() < 0.0f)
vel = -restitution_combined * (vel - g * dt);
// If velocity changes from positive to negative, we trace the height
bool positive_after = vel.GetY() >= 0.0f;
if (positive_before && !positive_after)
Trace("Iteration %5d - Bounce height: %f", i, pos.GetY());
// Update position
// Equivalent to PhysicsSystem::JobIntegrateVelocity
pos += vel * dt;
}
You're right, I didn't simulate it for long enough. It is indeed the same thing. At some point the velocity that we gain in one time step due to gravity equals the velocity we lose during the bounce and we end up in a steady state. Your solution is similar to what I had in mind. And yes, the Jolt code is a bit more involved than this simple example, so it needs a bit more thought.
When I set everything to 0.0 but 1 rigid body to restitution 1.0 every next bounce goes higher. Is it intended behaviour? Usually, it is bouncing on the same height in other physics engines.
git patch with example 0001-restitution-bug.patch