SAT is pretty fast for boxes, and also correct in all cases. You could have an intersection in which no corners were inside the other box.
There isn't really a contact point but an overlap region. However, what I do is test the boxes in turn and find the maximum extent of the other box against the axis being tested. After a final check that the point is inside the other box I have up to two points, which allows stable resting contact. But if you just want one you could take the average.
Right, but I'm fairly confident that those cases will never happen, and I would be happier with a fast algorithm that is correct in almost all practical scenarios than a slower one which is perfectly correct all 100% of the time. However, I'll be very happy if the 100% perfect solution is faster.
Actually, the incorrectness of the algorithm was a little bit useful for me, before implementing SAT, my game generated an NPC and placed it exactly on top of the player. since they were exactly the same size, no collision was registered, allowing me to drive away (as long as I drove perfectly straight) and then test collisions from different angles. Now that I'm using SAT, I need to generate the NPC elsewhere. (which I needed to implement later anyway)
I realized that after posting, there is no real contact point, the contact point is a bit of an illusion. However, I do need to choose a point somewhere in that region, since I need to apply the forces somewhere. The code that I am using right now actually still does both types of collision checking... SAT for separation axis, and the corner method for point of contact point. Obviously, that needs to be changed eventually. My old algorithm uses the point which is contained inside the other object as the contact point.
Although, my SAT implementation has some trouble. It finds collisions correctly, but does not give the overlap correctly, and does not return the correct axis.
My code looks like this if you want to look at it:
void SATgetMinMax(struct vehicle *obj, VEC2 *axis, double *min, double *max)
{
double mina,maxa;
VEC2 tmp,corner;
setVEC(obj->img.w/2,obj->img.h/2,&tmp);
relativeToWorld(obj,&tmp,&corner);
addVEC(&corner,&obj->chassis.coord,&corner);
mina=dotVEC(&corner,axis);
maxa=mina;
setVEC(-obj->img.w/2,obj->img.h/2,&tmp);
relativeToWorld(obj,&tmp,&corner);
addVEC(&corner,&obj->chassis.coord,&corner);
if (dotVEC(&corner,axis)<mina) mina=dotVEC(&corner,axis);
if (dotVEC(&corner,axis)>maxa) maxa=dotVEC(&corner,axis);
setVEC(-obj->img.w/2,-obj->img.h/2,&tmp);
relativeToWorld(obj,&tmp,&corner);
addVEC(&corner,&obj->chassis.coord,&corner);
if (dotVEC(&corner,axis)<mina) mina=dotVEC(&corner,axis);
if (dotVEC(&corner,axis)>maxa) maxa=dotVEC(&corner,axis);
setVEC(obj->img.w/2,-obj->img.h/2,&tmp);
relativeToWorld(obj,&tmp,&corner);
addVEC(&corner,&obj->chassis.coord,&corner);
if (dotVEC(&corner,axis)<mina) mina=dotVEC(&corner,axis);
if (dotVEC(&corner,axis)>maxa) maxa=dotVEC(&corner,axis);
*min=mina;
*max=maxa;
}
//Check for collisions with SAT
int SATXvehicles(struct vehicle *a, struct vehicle *b, VEC2 *over)
{
int i;
double overlap,
mina,maxa,
minb,maxb;
VEC2 tmp,
axis, saxis,
corner;
//Axis to check
setVEC(1.0,0.0,&tmp);
relativeToWorld(a,&tmp,&axis);
//Find min/max values on axis
SATgetMinMax(a,&axis,&mina,&maxa);
SATgetMinMax(b,&axis,&minb,&maxb);
//Check for overlap
if (!Xline1d(mina,maxa,minb,maxb))
return 0;
else
{
overlap=minb-maxa;
setVEC(-axis.y,axis.x,&saxis);
}
//Axis to check
setVEC(0.0,1.0,&tmp);
relativeToWorld(a,&tmp,&axis);
//Find min/max values on axis
SATgetMinMax(a,&axis,&mina,&maxa);
SATgetMinMax(b,&axis,&minb,&maxb);
//Check for overlap
if (!Xline1d(mina,maxa,minb,maxb))
return 0;
else
{
if (minb-maxa>overlap)
{
overlap=minb-maxa;
setVEC(-axis.y,axis.x,&saxis);
}
}
//Axis to check
setVEC(1.0,0.0,&tmp);
relativeToWorld(b,&tmp,&axis);
//Find min/max values on axis
SATgetMinMax(a,&axis,&mina,&maxa);
SATgetMinMax(b,&axis,&minb,&maxb);
//Check for overlap
if (!Xline1d(mina,maxa,minb,maxb))
return 0;
{
if (minb-maxa>overlap)
{
overlap=minb-maxa;
setVEC(-axis.y,axis.x,&saxis);
}
}
//Axis to check
setVEC(0.0,1.0,&tmp);
relativeToWorld(b,&tmp,&axis);
//Find min/max values on axis
SATgetMinMax(a,&axis,&mina,&maxa);
SATgetMinMax(b,&axis,&minb,&maxb);
//Check for overlap
if (!Xline1d(mina,maxa,minb,maxb))
return 0;
{
if (minb-maxa>overlap)
{
overlap=minb-maxa;
setVEC(-axis.y,axis.x,&saxis);
}
}
copyVEC(&saxis,over);
multiplyVEC(over,overlap,over);
multiplyVEC(over,5.0,over);
return 1;
}