Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

The Separating Axis Theorem (SAT) makes it simple to determine the Minimum Translation Vector, i.e., the shortest vector that can separate two colliding objects. However, what I need is the vector that separates the objects along the vector that the penetrating object is moving (i.e. the contact point).

I drew a picture to help clarify. There is one box, moving from the before to the after position. In its after position, it intersects the grey polygon. SAT can easily return the MTV, which is the red vector. I am looking to calculate the blue vector.

SAT diagram

My current solution performs a binary search between the before and after positions until the length of the blue vector is known to a certain threshold. It works but it's a very expensive calculation since the collision between shapes needs to be recalculated every loop.

Is there a simpler and/or more efficient way to find the contact point vector?

share|improve this question
1  
Are you dead set on using SAT? Algorithms like MPR (Minkowski Portal Refinement) can find the contact manifold directly. With SAT and GJK, you need a separate algorithm to calculate the contact points. –  Sean Middleditch Apr 6 '12 at 10:46
    
add comment

3 Answers

up vote 4 down vote accepted

What you're talking about is fairly difficult if you structure it as first moving an object, then testing for collision, then backing off until you're out of the object. It's probably better to think of this as a dynamic intersection test: a moving object against a stationary object.

Luckily, separating axis tests can help you here! Here's a description of the algorithm, courtesy of Ron Levine:

The algorithm goes like this. You work with the relative velocity vector of the two convex bodies. Projecting each of the two bodies and the relative velocity vector onto a particular separating axis at t₀ gives two 1-D intervals and a 1-D velocity, such that it is easy to tell whether the two intervals intersect, and if not, whether they are moving apart or moving together. If they are separated and moving apart on any of the separating axes (or, in fact, on any axis whatever), then you know that there is no future collision. If on any separating axis the two projected intervals intersect at t₀ or are separated and are moving together, then it is easy to compute (by two simple 1D linear expressions) the earliest future time at which the two intervals will first intersect and (assuming continuing rectilinear motion) the latest future time at which the two intervals will last intersect and begin moving apart. (If they are intersecting at t₀ then the earliest future intersection time is t₀). Do this for at most all the separating axes. If the maximum over all the axes of the earliest future intersection time is less than the minimum over all the axes of the latest future intersection time then that maximum earliest future intersection time is the exact time of first collision of the two 3D polyhedra, otherwise there is no collision in the future.

In other words, you loop through all the axes that you normally would in a static separating axis test. Instead of early-outing if you find no overlap, you keep going and check the projected velocity of the moving object. If it's moving away from the static object, then you early-out. Otherwise, you can solve for the earliest and latest time of contact fairly easily (it's one 1D interval moving towards another 1D interval). If you do that for all axes and keep the maximum of the earliest intersection time and the minimum of the latest intersection time, then you know if your moving object will hit the static object, as well as when. So you can advance your moving object exactly up to the point at which it will hit the static object.

Here's some rough and entirely unverified pseudocode for the algorithm:

t_min := +∞
t_max := -∞
foreach axis in potential_separating_axes
    a_min := +∞
    a_max := -∞
    foreach vertex in a.vertices
        a_min = min(a_min, vertex · axis)
        a_max = max(a_max, vertex · axis)
    b_min := +∞
    b_max := -∞
    foreach vertex in b.vertices
        b_min = min(b_min, vertex · axis)
        b_max = max(b_max, vertex · axis)
    v := b.velocity · axis
    if v > 0 then
        if a_max < b_min then
            return no_intersection
        else if (a_min < b_min < a_max) or (b_min < a_min < b_max) then
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, 0)
        else
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, (a_min - b_max) / v)
    else if v < 0 then
        // repeat the above case with a and b swapped
    else if v = 0 then
        if a_min < b_max and b_min < a_max then
            t_min = min(t_min, 0)
            t_max = max(t_max, 0)
        else
            return no_intersection
if t_max < t_min then
    // advance b by b.velocity * t_max
    return intersection
else
    return no_intersection

Here's a Gamasutra article talking about this implemented for a few different primitive tests. Note that just like SAT, this requires convex objects.

Also, this is a fair bit more complicated than a simple separating axis test. Be absolutely sure you need it before you try it. A very large number of games simply push the objects out of each other along the minimum translation vector, because they simply don't penetrate very far into each other on any given frame and it's pretty much unnoticeable visually.

share|improve this answer
1  
This is all very cool, but it didn't directly answer the question about calculating the contact manifold. Also, if I understand it correctly, this answer only works with linear velocity, and hence can't support rotating objects; not sure if the question asker wants that or not. –  Sean Middleditch Apr 6 '12 at 10:39
    
Thank you, this was enlightening. –  Kai Apr 6 '12 at 12:22
    
@seanmiddleditch That's true, it neglects rotations over the frame. You have to rotate instantaneously at the beginning. But no method that I know short of conservative advancement actually deals accurately with rotations. Given no rotation, though, it produces a better estimate of the contact point. –  John Calsbeek Apr 6 '12 at 15:47
add comment

You want to use polygon clipping. This is best explained with pictures, which I don't have, but this guy did, so I'll let him explain it.

http://www.codezealot.org/archives/394

The contact manifold will return a point on one of the objects that is "most responsible" for the collision, not the direct point of collision. However, you don't really need that direct collision point. You can simply push the objects apart using the penetration depth and normal you already have, and use the contact manifold to apply other physical affects (make the box tumble/roll down the slope, for instance).

Note that your picture illustrates a small problem: the point on the blue vector you're asking for will not be found in any physical simulation, because that isn't actually where the box would hit. The box would hit with it's bottom-left corner somewhere further up the slope as just a small bit of the corner penetrates.

The penetration depth will be relatively small, and simply pushing the box out of the slope along the penetration normal will put the box close enough to the "correct" position to be almost unnoticeable in practice, especially if the box is going to bounce, tumble, or slide afterward anyway.

share|improve this answer
add comment

Just project the MAT Vector onto the direction Vector. The resulting Vector can be added to the Direction Vector to compensate the penetration. Project it the same way, as you do on the Axis when doing the SAT. This sets the Object exactly on the position that it touches the other object. Add a small epsilon to fight floating point problems.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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