The mystery of the see-saw bouncing polygon

Started by
6 comments, last by Zzet 16 years, 10 months ago
Well, I have a 2D collision system working great (major thanks to oliii!), however I have one particular problem...actually, I won't describe it, i'll show you: http://www.zen111944.zen.co.uk/vids/weirdbouncing_xvid.avi (1.82MB, XviD) Basically this problem occurs whenever a polygon of 5 or more edges comes to rest on an immovable object. Friction is about 0.2 and elasticity is really low at 0.1, and the masses are all quite low. I have a theory that if the bounds of the polygon on the collision plane are beyond that of the interval used for calculating contacts, it can cause this to occur, but I confess I don't know how to proceed if that's true. Any help is appreciated, and apologies for creating a new thread about this instead of continuing my old one, but it's quite a different problem I think!
Advertisement
How are you responding to collisions? Are you applying impulses to contact points, or something else? Bouncing shapes is actually a pretty common problem for collision response on immobile objects, especially for impulse based methods, but that it would only occur on many-sided polygons seems odd.
[size=2]Darwinbots - [size=2]Artificial life simulation
The reason why it breaks for objects with more than 5 sides is because at that point, it takes a relatively small amount of force to cause the object to roll (think octagon vs. triangle). This bouncing is there on the triangles, but it is below the threshold determined by the way the objects is shaped.

How are you resolving collisions? How are you calculating the moments of inertia for the objects? this is important information to know if we are going to help you.
Each time a contact occurs, the velocity of the point of contact on each object is calculated and the response given as an impulse appropriate to the friction/elasticity/inertia/mass. Inertia for each polygon is pre-calculated and stored as a variable, and never changes. Could this be the problem?

In the example video above, each shape is created and in doing so, the inertia is calculated along with the inverse inertia (used in the actual calculation). The method I use:

private void calculateInertia()    {        float denom = 0.0f;        float numer = 0.0f;        int Anum = poly.vertices.length;        for(int j = Anum-1, i = 0; i < Anum; j = i, i ++)        {            Vector P0 = poly.vertices[j];            Vector P1 = poly.vertices;            float a = Math.abs(P0.getCrossProduct(P1));            float b = (P1.getDotProduct(P1) + P1.getDotProduct(P0) + P0.getDotProduct(P0));            denom += (a * b);            numer += a;        }        inertia = (mass / 6.0f) * (denom / numer);        if (inertia > 0.0001f) { inverseInertia=1/inertia; }        else { inverseInertia = 0.0f; }    }


poly refers to the polygon I use to display the shape, which has an array of vertices (position vectors). inertia and inverseInertia are member variables.

I grabbed this from a forum post explaining about inertia; unfortunately I forget which one. Should the inertia be calculated each time for the collision point?
Your method of calculating intertia looks sound, so that's not the problem, and you shouldn't need to recalculate it every frame or when there is a collision (because there is only one rotational axis in 2D).

Can you explain more explicitly how you are resolving collisions, specifically with how you calculate the relative velocity at the point of contact, as well as the impulse determination.

There is also a posibility that this might have something to do with the point/normal you are supplying the collision response system. How are you doing that? also, do you allow for multiple contact points between two objects (such as when an edge rests on an edge).
There are more or less two methods that govern the entire response in my code. The first calculates the impulse, the second applies it to the object.

The first method; impulse calculation given two physics-responding objects (AbstractPhysicsObject a and b), a relative contact point on each of those objects (Vector aContact and bContact) and the minimum translation vector that is basically the normal of the collision (Vector N).

public static void collideBodies(AbstractPhysicsObject a, AbstractPhysicsObject b, Vector aContact, Vector bContact, Vector N)    {        Vector N = Nun.getDivide((float) Math.sqrt(Nun.getDotProduct(Nun)));        Utilities.log("A contact="+aContact+" B contact="+bContact+" N="+N);        //contact points are already relative to the centre of mass        Vector Ra = aContact;        Vector Rb = bContact;        //velocities at the contact point        Vector Va = a.velocity.getAdd(Ra.perp().getMultiply(a.angularVelocity));        Vector Vb = b.velocity.getAdd(Rb.perp().getMultiply(b.angularVelocity));        Utilities.log("A contact velocity="+Va+" B contact velocity="+Vb);        //relative velocities        Vector V = (Va.getSubtract(Vb));        float vn = V.getDotProduct(N); //velocity along normal, or impact velocity        Utilities.log("Relative impact velocity = "+V+" ...along normal = "+vn);        if (vn > 0.0)        {            //objects moving away from eachother            Utilities.log("Objects are moving away, no collision.");            return;        }        //collision impulse        float numer = -(1.0f+ Constants.ELASTICITY)*vn;        float denom0 = (a.inverseMass + b.inverseMass);        float denom1 = (a.inverseInertia * Ra.getCrossProduct(N) * Ra.getCrossProduct(N));        float denom2 = (b.inverseInertia * Rb.getCrossProduct(N) * Rb.getCrossProduct(N));        float i = numer/(denom0+denom1+denom2);        Vector Ir = N.getMultiply(i);       //vector collision impulse        Utilities.log("Collision impulse = "+Ir);        //friction impulse        Vector Vt = V.getSubtract(N.getMultiply(vn));   //velocity projected in plane of collision        Vector If = Vt.getMultiply(-Constants.FRICTION/(a.inverseMass+b.inverseMass));  //friction impulse        Utilities.log("Friction impulse = "+If);        //add total collision impulse        Vector I = (Ir.getAdd(If));        Utilities.log("Final collision impulse = "+I);        //apply impulses        a.addImpulse(aContact,I);        b.addImpulse(bContact,I.getReverse());    }


The second method called addImpulse basically affects the object according to the impulse and location of application on the object:

public void addImpulse(Vector P, Vector J)    {        //linear velocity change        velocity = velocity.getAdd(J.getMultiply(inverseMass));        //angular velocity change        angularVelocity = angularVelocity + P.getCrossProduct(J)*inverseInertia;        }


An edge coming into contact with an edge results in at least one point being inside another polygon, so whatever happens the world contact point is averaged and ends up being in the middle of the overlap of the two objects. So if a square moves into a square edge to edge, the minimum translation vector is the result of averaging the two points (or four, if the squares line up exactly).
However, if an edge penetrates a large edge, the smaller object's points are calculated then the average of those is projected onto the collision edge of the larger object. This is what is happening with the blue square in the video, which might explain why it bounces initially. However it doesn't explain (to me at least) why the square and triangle come to rest, and the hexagon doesn't :)

I think your problem might be that you don't allow more than one contact point for an edge-edge collision. What you need to do is to see if the colliding edges are almost parallel. You can do this by taking the cross product of the two vectors defining the direction of the egdes. If the magnitude of that vector is less than a certain value (0.001 for example), then you must generate two contact points. This is necessary for stable resting contact.

Another factor is the way that you are generating contact points. Instead of just generating a single point that is the "average" contact point, try just generating 2 contact points, one for each object. For instance, in a vertex-edge collision, the point on object 1 would be the vertex, and the point on object 2 would be the projection of the vertex on the edge. This will make it so that the angular impulse will be correct. I do this in my engine and it works well.

Other than that, your impulse code looks correct. I think you really need to focus on allowing more than one contact point at a time.
How can I ignore advice given thrice? :D I'll give it a shot, many thanks!

This topic is closed to new replies.

Advertisement