Gravity, simple circle collision and calculating impulses.

Started by
5 comments, last by remigius 14 years, 1 month ago
I have figured out the exact contact points and the velocity for my rigid bodies but something is still not correct with my impulse calculation as something as simple as two interpenetrating circles do not resolve penetration correctly based on impulses. I calculate the impulse as follows:

                //================================================================================
                //-- Compute Impulse (Friction and restitution) ----------------------------------
                //================================================================================
                //
                //									-(1+e)(Vr • n)
                //			j =  -------------------------------------------------------
                //			     (1/Ma + 1/Mb) + (Ia' * (ra x n)^2) + (Ib' * (rb x n)^2)
                //
                // Where:
                //
                // j   = Impulse
                // e   = Coefficient of Restitution
                // Vr  = Relative velocity (A.Velocity - B.Velocity)
                // n   = Collision normal
                // Ma  = Mass of A
                // Mb  = Mass of B
                // Ia' = Inverse Moment of Inertia for A
                // Ib' = Inverse Moment of Inertia for B

                RigidBody2D a = contact.body[0];
                RigidBody2D b = contact.body[1];

                float ma = a.Mass_Inverse;
                float mb = b.Mass_Inverse;

                // (1/Ma + 1/Mb)
                float m = ma + mb;

                // Coefficient of restitution
                float e = 0.1f;

                // Calculate relative velocity
                Vector2 velocity_Relative = a.Velocity - b.Velocity;            // Velocity under gravity = (0, -9.81f)

                Vector2 ra = contact.point[0] - a.Position;
                Vector2 rb = contact.point[1] - b.Position;

                float raXn = Math_Util.Cross(ra, contact.normal);
                float rbXn = Math_Util.Cross(rb, contact.normal);

                // (Ia' * (ra x n)^2)
                float t0 = raXn * raXn * a.Inertia_Inverse;

                // (Ib' * (rb x n)^2)
                float t1 = rbXn * rbXn * b.Inertia_Inverse;

                // (1/Ma + 1/Mb) + (Ia' * (ra x n)^2) + (Ib' * (rb x n)^2)
                float denom = m + t0 + t1;

                // Calculate scalar impulse
                float j = -(1f + e) * Vector2.Dot(velocity_Relative, contact.normal);
                j /= denom;

                // Calculate vector impulse
                Vector2 impulse = j * contact.normal;

                // ===========================================
                // -[FRICTION]--------------------------------
                // ===========================================
                // Coefficient of friction
                //float f = 0f;

                if (!a.HasInfiniteMass)
                {
                    // Add impulse force at contact point for each body
                    a.ApplyImpulse(impulse, contact.point[0]);

                    if (a.Velocity.LengthSquared() < 0.001f)
                    {
                        a.Velocity = Vector2.Zero;
                    }
                }

                if (!b.HasInfiniteMass)
                {
                    // Negate the force for the opposite body (Newton's Third Law)
                    b.ApplyImpulse(-impulse, contact.point[1]);

                    if (b.Velocity.LengthSquared() < 0.001f)
                    {
                        b.Velocity = Vector2.Zero;
                    }
                }


I have tried zeroing out the velocity vector if it is too small due to floating point inaccuracies but that doesn't help. The only force acting here is gravity and that still allows one shape to intersect another. The shape positions are updated according to the body positions in the rigid body integrate method:

        public void Integrate(float duration)
        {
            // ==========================================================================================================
            // --[Linear]------------------------------------------------------------------------------------------------
            // ==========================================================================================================
            
            // Update acceleration from accumulated forces
            // F = M * A
            // A = F / M
            Vector2 acceleration = force_Accumulated * mass_Inverse;

            // Update linear velocity from the acceleration
            // V = V + A * dT
            velocity_Linear += acceleration * duration;

            // Update linear position
            // P = P + V * dT         
            position += velocity_Linear * duration;

            // ==========================================================================================================
            // --[Angular]-----------------------------------------------------------------------------------------------
            // ==========================================================================================================

            // aA = T / I
            float acceleration_Angular = torque_Accumulated * shape.Inertia_Inverse;
            
            // aV = aV + aA * dT
            velocity_Angular += acceleration_Angular * duration;

            // R = R + aV * dT 
            rotation += velocity_Angular * duration;

            // ==========================================================================================================

            // Clear the accumulated forces
            force_Accumulated = Vector2.Zero;
            torque_Accumulated = 0f;

            // Update collision shape if it exists
            if (shape != null)
            {
                shape.Position = position;
                shape.Rotation = rotation;
            }
        }


This is where the force due to gravity pulls one shape inside the other very slowly as if the shape were made of quicksand. I've tried everything I can think of and I've simply no idea how to stop this from occuring. Collision detection works perfectly based on shape position alone, so something about this impulse process just isn't correct. I've combed through a lot of oliii's code for ideas but I still can't find any obvious errors in my code based on his. Can anyone help here, this is driving me crazy! Thank you.
Advertisement
You're back for more punishment, huh?

I think it should be position = v0*t + 1/2*a*t2.

In your Integrate function, try updating your position using the above before you update the velocity.

Yeah, I know. I missed that before.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:
You're back for more punishment, huh?


It would appear so! :)

Thank you for your suggestions, I have now updated the linear motion part of the integrator:

            // ==========================================================================================================            // --[Linear]------------------------------------------------------------------------------------------------            // ==========================================================================================================                        // Update acceleration from accumulated forces            // F = M * A            // A = F / M            Vector2 acceleration = force_Accumulated * mass_Inverse;            // Update linear position            // P = P + V * dT                     //position += velocity_Linear * duration;            // dP = V0 * t + 0.5 * a * t^2            position += velocity_Linear * duration + (0.5f * acceleration * duration * duration);            // Update linear velocity from the acceleration            // V = V + A * dT            velocity_Linear += acceleration * duration;


Unfortunately this makes no difference to the outcome. Any circle can still sink slowly into another non moving circle whilst only under the force of gravity. Other applied forces will have the same effect.

It just doesn't make any sense to me why this is happening but I can record a video of what is going on if that helps?
You might try setting some of the variables in your equation for j to values such that they don't make a contribution to j. Then add them back in, one at a time to see where the behavior comes from.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I've removed gravity altogether and added 0.02f to the scalar value of j.

When applying a force to one circle, moving it towards a static circle, the moving circle will not sink into the static one.

Instead, when I stop applying the force to the moving circle, it will then move in the direction of the impulse with a velocity of precisely 0.02f.

The same goes for j += 0.06f, j+= 0.09f, etc

The velocity will not change unless another force is applied.

                // Calculate scalar impulse                float j = -(1f + e) * Vector2.Dot(velocity_Relative, contact.normal);                j /= denom;                j += 0.02f;             // <---------------- Added this line


I'm not sure why this would occur though or why, when the small value is omitted, the circles overlap.

Any thoughts?
No-one knows how you want your code to work better than you do. Have you set breakpoints and stepped through your code, looking at variable values and checking that they're giving you the results you expect?

Output some debug values to your debug window while the program is running and see if it's doing what you want it to do. You have a very simple situation, if gravity is all you've got plugged. Does each line of code behave correctly?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


As a random thought, if the 'pull-in' is really, really slow you may be looking at numerical instability. Floating point and rounding errors are making your impulse lose to the constant gravity push. If that's the case, you might want to try limiting the number of updates you run each second to see if things get better.

You could also fudge it by doing something like j *= 1.01. This is better than the addition you're doing now, because it will vanish along with the impulse and thus doesn't cause the drift you're seeing now.
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!

This topic is closed to new replies.

Advertisement