4
\$\begingroup\$

I'm writing a rigid body simulation in C++, and have come across a problem when an object should come to rest on the ground.

Because gravity is being applied to it's velocity, the object starts sinking through the ground, or whatever else it's resting on.

To combat this, I am adjusting the position of the object, by moving it along the collision normal by the penetration depth. This has the drawback of making the object look like its vibrating, so it never comes to rest.

I read somewhere ages ago, that there is a way to counter this, by generating a force along the normal which will cancel out the force which is causing it to sink. A google search did not return any useful answers however. I suspect this is because I'm not asking the right question, but I don't know a more concise way to word it, so I'd appreciate if someone here has solved this, and could steer me in the right direction.

\$\endgroup\$
  • \$\begingroup\$ You need some kind of "at rest" field that disables gravity after it has penetrated the "ground" and been nudged back. Then re-enables it when a raycast below determines that it is no longer grounded. \$\endgroup\$ – Draco18s Jan 12 '16 at 16:37
  • \$\begingroup\$ I assume you've already researched Normal Force? \$\endgroup\$ – DMGregory Jan 12 '16 at 17:45
  • \$\begingroup\$ @Draco18s I was thinking that, but I need it to do the same when, for example, a couple of cubes are stacked on each other. \$\endgroup\$ – Ian Young Jan 13 '16 at 1:30
  • \$\begingroup\$ @DMGregory No I hadn't seen the normal force. I'll look into it, thanks. \$\endgroup\$ – Ian Young Jan 13 '16 at 1:30
3
\$\begingroup\$

You need to detect when the object has come to "rest" and turn off the physics (gravity included) on it until some force gets applied to it again.

You'll have to pick a threshold of "rest" that's larger than most cases of vibration but small enough to let objects bounce a few times and slide a bit.

How to do this exactly depends on the physics engine and the objects in question. Weight, bounciness, friction, physics frame rate, and personal taste will change what threshold values are acceptable.


There isn't a solution that works 100% due to rounding errors and the odd cases where the object is bouncing off multiple walls.

Often people have to change their level design a bit to alleviate the odd cases such as avoiding creating very concave corners or by placing invisible collision planes to stop objects from getting into those corners.

With game physics it's not a question of getting it perfect but getting it to a point where it's acceptable and doesn't ruin the fun.

\$\endgroup\$
1
\$\begingroup\$

I finally found a solution, which does not involved the rather crude method of correcting positions.

This completely eliminated both the sinking and the jitter.

Sinking

Sinking is caused by the resultant force being insufficient to push the objects apart, in the case of an object coming to "rest" on a stationary object. So we need an additional force to help. This is the normal force, and is proportional to the amount the two bodies are interpenetrating, and the density of the bodies: i.e. the more the objects interpenetrate, the harder they push back:

penetrationMultiplier = 1 + penetrationDepth * density;
Ft = F * penetrationMultiplier;

Density is a value for each body between 0.0 and 1.0. With very low penetration depths, the magnitude of the penetration value will not be much over 1.0, but will increase in magnitude as penetration depth increases.

Jitter

Jitter is caused by a body never fully coming to rest, and keeps bouncing until the you reach the limits of floating point precision. To solve this, we must set a minimum limit on motion of bodies, below which, the body can be deemed to be "at rest". Let us call this value epsilon. When a body is at rest, only a force of magnitude greater than epsilon will cause the body to no longer be at rest.

The calculation to place a body at rest, or "asleep" applies to both linear and angular motion, and is applied at the point of updating positions by velocity * deltaTime:

void update(float deltaTime)
{
    if(m_awake == true)
    {
        if ( length(velocity) < epsilon )
        {
            velocity = vec3(0.0);
            m_awake = false;
        }
        else
        {
            position += (velocity * deltaTime) * damping;
            rotation += (angularvelocity * deltaTime) * damping;
        }
    }
}

Damping is value which will sap a tiny amount of energy from a bodies angular and linear velocities over time, which will allow a body to eventually lose energy to the point of epsilon coming into effect.

Then when a force is applied, to potentially wake it up:

void applyForce(vec3 force, vec3 contactPoint)
{
    if (length(force) > epsilon)
    {
        m_awake = true;
        // apply force as normal, calculating new linear and angular motion
    }
}

Eventually, due to energy losses via restitution, damping and inertia/momentum calculations, the magnitude of the impulse will be insufficient to cause a noticeable change in linear or angular velocity, and thus the body will go to, and remain, asleep.

\$\endgroup\$
  • \$\begingroup\$ I am having the same problem and managed to prevent the sinking but the jittering is still present (seems to be related to the size of the percentage slop) - having tried the solution from the link you finally found. Is there a specific order to implementing the position correction? It seems after every correction, Gravity brings the RigidBody back down to the ground and a new collision is detected and acting on.. The ONLY way I am able to control the jittering is to "disable" the body from gravity after a pre-defined number of collision resolution cycles! To me, it doesn't quite seem right. \$\endgroup\$ – Nas25 Apr 12 at 21:29
  • \$\begingroup\$ This answer would be better if it included a summary of the insights you hope a reader will glean from that link, so the answer remains useful even if the link's contents change or the page becomes unavailable in future. \$\endgroup\$ – DMGregory Apr 12 at 21:39
  • \$\begingroup\$ As I found a better way of doing it, I've completely rewritten my answer, which should be far more helpful. \$\endgroup\$ – Ian Young Apr 21 at 15:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.