Search code examples
sorting3ddistancetopology

how to sort objects for Guendelman shock propagation?


This is a question regarding forming a topographical tree in 3D. A bit of context: I have a physics engine where I have bodies and collision points and some constraints. This is not homework, but an experiment in a multi-threading.

I need to sort the bodies in a bottom-to-top fashion with groups of objects belonging to layers like in this document: See section on "shock-propagation" http://www2.imm.dtu.dk/visiondag/VD05/graphical/slides/kenny.pdf

sorting tree

the pseudocode he uses to describe how to iterate over the tree makes perfect sense:

shock-propagation(algorithm A)
compute contact graph
for each stack layer in bottom up order
     fixate bottom-most objects of layer
     apply algorithm A to layer
     un-fixate bottom-most objects of layer
next layer

I already have algorithm A figured out (my impulse code). What would the pseudocode look like for tree/layer sorting (topo sort?) with a list of 3D points?

I.E., I don't know where to stop/begin the next "rung" or "branch". I guess I could just chunk it up by y position, but that seems clunky and error prone. Do I look into topographical sorting? I don't really know how to go about this in 3D. How would I get "edges" for a topo sort, if that's the way to do it?

Am I over thinking this and I just "connect the dots" by finding point p1 then the least distant next point p2 where p2.y > p1.y ? I see a problem here where p1 distance from p0 could be greater than p2 using pure distances, which would lead to a bad sort.


Solution

  • i just tackled this myself.

    I found an example of how to accomplish this in the downloadable source code linked to this paper:

    http://www-cs-students.stanford.edu/~eparker/files/PhysicsEngine/

    Specifically the WorldState.cs file starting at line 221.

    But the idea being that you assign all static objects with the level of -1 and each other object with a different default level of say -2. Then for each collision with the bodies at level -1 you add the body it collided with to a list and set its level to 0.

    After that using a while loop while(list.Count > 0) check for the bodies that collide with it and set there levels to the body.level + 1.

    After that, for each body in the simulation that still has the default level (i said -2 earlier) set its level to the highest level.

    There are a few more fine details, but looking at the code in the example will explain it way better than i ever could.

    Hope it helps!

    Relevant Code from Evan Parker's code. [Stanford]

    {{{
    
    // topological sort (bfs)
                // TODO check this
                int max_level = -1;
                while (queue.Count > 0)
                {
                    RigidBody a = queue.Dequeue() as RigidBody;
                    //Console.Out.WriteLine("considering collisions with '{0}'", a.Name);
                    if (a.level > max_level) max_level = a.level;
                    foreach (CollisionPair cp in a.collisions)
                    {
                        RigidBody b = (cp.body[0] == a ? cp.body[1] : cp.body[0]);
                        //Console.Out.WriteLine("considering collision between '{0}' and '{1}'", a.Name, b.Name);
                        if (!b.levelSet)
                        {
                            b.level = a.level + 1;
                            b.levelSet = true;
                            queue.Enqueue(b);
                            //Console.Out.WriteLine("found body '{0}' in level {1}", b.Name, b.level);
                        }
                    }
                }
    
                int num_levels = max_level + 1;
    
                //Console.WriteLine("num_levels = {0}", num_levels);
    
                ArrayList[] bodiesAtLevel = new ArrayList[num_levels];
                ArrayList[] collisionsAtLevel = new ArrayList[num_levels];
                for (int i = 0; i < num_levels; i++)
                {
                    bodiesAtLevel[i] = new ArrayList();
                    collisionsAtLevel[i] = new ArrayList();
                }
    
                for (int i = 0; i < bodies.GetNumBodies(); i++)
                {
                    RigidBody a = bodies.GetBody(i);
                    if (!a.levelSet || a.level < 0) continue; // either a static body or no contacts
    
                    // add a to a's level
                    bodiesAtLevel[a.level].Add(a);
    
                    // add collisions involving a to a's level
                    foreach (CollisionPair cp in a.collisions)
                    {
                        RigidBody b = (cp.body[0] == a ? cp.body[1] : cp.body[0]);
                        if (b.level <= a.level) // contact with object at or below the same level as a
                        {
                            // make sure not to add duplicate collisions
                            bool found = false;
                            foreach (CollisionPair cp2 in collisionsAtLevel[a.level])
                                if (cp == cp2) found = true;
    
                            if (!found) collisionsAtLevel[a.level].Add(cp);
                        }
                    }
                }
    
                for (int step = 0; step < num_contact_steps; step++)
                {
                    for (int level = 0; level < num_levels; level++)
                    {
                        // process all contacts
                        foreach (CollisionPair cp in collisionsAtLevel[level])
                        {
                            cp.ResolveContact(dt, (num_contact_steps - step - 1) * -1.0f/num_contact_steps);
                        }
                    }
                }
    
    }}}