Collision Detection & Response

CS 480 Lecture, Dr. Lawlor

"Collision detection" is figuring out when your simulated objects are going to pass through one another.

The simplest form of collision detection is "pairwise" collision detection, involving just two objects.  The simplest pairwise collision detection question is "Is this particle inside this triangle?". 

The easy way to determine this is to notice that if a point is inside a triangle, it's also inside each edge of the triangle.  We can determine which side of an edge AB a point C is sitting on with a "counterclockwise test": is the point C counterclockwise from point B, as viewed from point A?  I usually figure this out by thinking of cross products (does the angle ABC have a positive or negative sin?), but you can also motivate the exact same equation by looking at the equation of the line AB.
/* Counterclockwise test: return a positive value if, 
from a's point of view, c is counterclockwise of b.
Iff this returns zero, then the points are colinear.
*/
double ccw(const vec2 &a,const vec2 &b,const vec2 &c)
{
vec2 B=b-a, C=c-a;
return B.x*C.y-B.y*C.x;
}
Bottom line: to determine if a point is inside a triangle, do a counterclockwise test on that point versus each edge of the triangle.  If the point is inside all the edges of the triangle, the point is inside the triangle.

There are of course many other shapes (triangles, spheres, boxes, etc) that you might want to collision detect.  There are many libraries to detect such intersections, including the tiny but very C++'ey GPL Wykobi, every possible intersection function by Magic Software,  and (mostly for triangulations) the heavy-duty Computation Geometry Algorithms Library.

"Broad Phase" Collision Detection

For very small problems, all you need is the narrow-phase collision detection above.  But the problem is checking every particle against every triangle is O(P*T), which grows pretty fast.  For example, with 10^3 particles and triangles, you have a managable 10^6 point-in-triangle tests.  For 10^6 particles and triangles, you have 10^12 point-in-triangle tests (a trillion tests), which is going to take way longer than anything else in your program.

A very simple solution to speed up collision detection is to not test every particle against every triangle.  For example, we can build a grid, sift particles into their grid cell, and then only compare each triangle with the grid cells the triangle touches.  This works great in 1D, 2D, 3D, or even higher dimensions, and is pretty easy to implement with a dense array if your domain is bounded.  If objects can move arbitrarily far away, you don't want a regular dense grid, but a sparse grid, like a hashtable from grid cell to list of particles.

The bottom line is that you make a data structure that keeps a list of particles at each x,y grid cell:
        /* For each x,y, a list of nodes in that grid cell */
std::vector< std::vector< std::vector<int> > > grid;
Note you could make a sparse grid with std::map< std::pair<int x,int y>, std::vector<int particleNumber> >.

Then you "sift" particles into the grid:
        /* Add particles to my grid */
void sift(const particle *pts,int npts) {
for (int p=0;p<npts;p++) {
int y=grid_y(pts[p].P);
int x=grid_x(pts[p].P);
grid[y][x].push_back(p);
}
}
Finally, you collision-detect triangles against the particles in all their grid cells:
        void hit(particle *pts,triangle &t){
/* Find the grid cells I touch, by looking at my corners */
int maxX=0,maxY=0;
int minX=gridsize-1,minY=gridsize-1;
for (int corn=0;corn<3;corn++) {
int y=grid_y(pts[t.corner[corn]].P);
int x=grid_x(pts[t.corner[corn]].P);
maxX=max(maxX,x); maxY=max(maxY,y);
minX=min(minX,x); minY=min(minY,y);
}
/* Check the points stored in each of my grid cells */
for (int y=minY;y<=maxY;y++)
for (int x=minX;x<=maxX;x++)
{
const std::vector<int> &list=grid[y][x];
for (unsigned int p=0;p<list.size();p++) {
collide(pts,t,list[p]);
}
}
}
In practice, you want to make darn sure that you're not reallocating the grid every timestep, or you'll be purely allocator-limited.  Also, if points can get outside the bounds of your grid, you need to check that before doing any grid lookup, or you'll access the vector out of bounds.  See my "480_gridcollide_fem" example program on the main course page.

I also did my Master's Thesis on a parallel implementation of 3D voxel-based collision acceleration.

Collision Response

OK, so you've found that two objects intersect.  What can you do?
In real life, intersections are resolved using forces.  Back in physics class, you computed the intersection force as a combination of the normal force (pushing into the surface) and the static or sliding friction force.  You can still do the same thing!