Adding vector from another moving object...

Started by
38 comments, last by mwkenna 12 years, 7 months ago
it would happen if the ball was clipping the box at a very shallow angle. In that case, the MTD vector might be not pointing down, but pointing to the right, as the amount of intersection on the X axis is actually lower than the amount of intersection in the Y axis. This is one of the major drawback of doing a SAT in static.

To get back to the example, the ball will be moving to the right, and the normal of collision vector will be pointing to the right. Since the ball velocity and the normal of collision are going in the same direction (dot product will be > 0), there will be no bounce.

                +------------+            /              y overlap = 5 pixel wide   /                |  v         |          /                | ........+------------+               |  |      |  |        /|               |  |     >|--|< x overlap = 3 pixel wide               |  |      |  |      /  |               +---------|--+     /   |                  ^      |       /    |                         |      /     |                         |     /      |                         |    /       |                         +---/--------+                            /                            / ////  x = 3//  y = 5//  => x < y, so MTD will point towards the right (x axis)//  => but velocity if going mostly up, so expect a downward collision normal//  => bummer//


If you *do* apply the response, the ball will bounce back towards the wall, and over several frames will appear to be stuck to the wall. Hence why I cut short the response code in that case, and the lack of collision response. YOu can try that by yourself.

several solutions.

1) split the trajectory of the ball into smaller timesteps to find a good MTD, like in your example where it will be aligned with the bottom wall. Although there will be genuine cases where this case will be perfectly valid and will not return any meaningful results.

2) do the SAT test in dynamics. But that's more complicated, and will stretch your vector maths a bit further. I've got several examples and docs around explaining the swept SAT.

3) Some form of hacks.

in any case, your collision detection and physics will have to be upgraded, and work on a swept basis (factor velocities in the collison detection tests, collision loop, where you have to find the first collision, apply response at each step, then find other collisions until the time left in search is exhausted). That would be the next step, but it depends how much time you want to spend on that thing.

Everything is better with Metal.

Advertisement
Well crud. I think I'm starting to run into the limitations that you're talking about, oliii. Methinks I'm gonna have to try your second suggestion:

Quote:2) do the SAT test in dynamics. But that's more complicated, and will stretch your vector maths a bit further. I've got several examples and docs around explaining the swept SAT.


Would you point me in the direction of those examples? Thanks!
"The crows seemed to be calling his name, thought Caw"
here, here, and here.

the first one has a doc explaining the algorithm (done in 3D). It also has a number of demos (press F1 to F7 if I remember corectly. all mouse controls and keys awsd). please, do the effort to read the doc, and dont rip out the code straight away. make sure you get it in your head first.

The second example is the 2D version. It's more what you'd like to achieve. It also has sphere-polygon collision (based on Raigan Burn's tutorial).

The third is an example, with added physics and contact point calculations.

Everything is better with Metal.

Thanks again for the info, oliii. I just wish I could bang it all into my head and understand it! I don't want to simply rip somebody else's code and tailor it for my game, because I don't learn anything that way. But since I code using generic C, I don't have a whole lot of experience in writing and understanding classes, and that makes it tough to understand some of the sample code. And it's REALLY hard to find websites that contain 'workable' code that goes along with their tutorials. The only useful sites and tuts have been the ones you've sent my way, and I really appreciate it!

Anyhoo, I'll get back to reading and see if I can figure out any of this stuff. Maybe I just need to take it in a little at a time... Wish me luck!

Thanks again!
"The crows seemed to be calling his name, thought Caw"
as I said in the docs, you start off with a ray-box intersection test. If you understand the principle, and since you know the SAT, you're basically 90% of the way there.

Everything is better with Metal.

OK, I think I'm starting to understand this...

But I'm having a problem understanding 'swept' and time-based collision detections. I'm really starting to understand 'static' collision detections though, so I guess I'm getting somewhere [smile]. Here's a pic of what I'm doing right now:



You can see the game object (ball) is traveling down and to the right. OldLoc[2] is the ball's x/y coordinates at the last frame, and NewLoc[2] is the ball's x/y coordinates on this frame. You can also see how the ball at NewLoc is colliding with the blue object; where it's at will cause an incorrect calculation of MTD when the SAT function is applied and push the ball down rather than the correct direction of left. I've been trying to understand 'swept' and time-based collisions, and this is all I understand:



If I can extrapolate the point where the ball actually comes in contact with the blue object, I can then nudge and bounce it the correct way. However, I can't figure how to get that extrapolated position. And I still don't really understand 'swept' collision detections at all... Can one of you guys point me to a tutorial or explanation on how I can learn how to do this? Since I'm working on a 2D game, if you could point me to a 2D tutorial or explanation then that would be AWESOME!

Thanks in advance for the help!
"The crows seemed to be calling his name, thought Caw"
You highlighted in your above post one of the pitfalls of static tests: even if tunneling is avoided, the results can still be less than satisfactory.

If you're looking for a quick fix, you could subdivide your step into small enough substeps that each would be small relative to the box dimensions. Although this probably wouldn't be a good approach where many objects are involved, in a simple setting I think it would be adequate.

However, as you note, swept tests may be a better answer. I think a good starting place would be this gamasutra article (free membership required) which is linked from the gdnet articles section. Among other things it covers swept AABBs in 3d, which can easily be adapted to 2d by dropping a dimension and would be (I think) exactly what you're looking for.

IMO, swept tests with SAT can be a little hard to get a handle on conceptually (again, IMO). Starting with axis-aligned boxes in 2d is a good way to get comfortable with it before moving to 3d and having to deal with cross-product axes and so on. It also may help to draw some pictures or create some diagrams such as the ones in your post.

If you've got the static SAT test down, you understand that an object can be projected onto an axis to yield an interval with a minimum and maximum value. Now imagine that the object is moving; it stands to reason then that the interval would be moving as well. How much and in what direction depends on the relationship between the object's velocity vector and the axis. For example, for an object moving perpendicular to the axis, the projected interval would be stationary (draw it if you're not sure).

Now imagine the interval associated with your moving box moving along the axis, and also a stationary interval which is the projection of an obstacle you wish to test against. With some algebra you can determine a) when the intervals will first intersect, and b) when they will stop intersecting.

Well, for such a simple test it actually gets a little complicated to explain, so I'll cut to the chase. In order for the objects to intersect over the time step, there must be some time interval during which they are overlapping on all potential separating axes. There are different ways you can keep track of this, but it all boils down to pretty much the same thing. The beginning of the time interval of possible intersection is the greatest of all the first times of intersection collected from the axes. The intuition is that the objects cannot possibly intersect until they have begun to overlap along all potential separating axes. The end of the time interval of possible intersection is the least of all last times of intersection collected from the axes. The intuition is that the objects cannot possibly intersect after they have stopped overlapping along any potential separating axis. If the time interval of possible intersection ends up being negative, i.e. min > max, they didn't intersect. Otherwise, min is the first time of intersection.

Ok, that post was too long. 'Hope some of it will be helpful, though.
Dookie, you've got it spot on there :)

ok, now, for the extrapolated (really, it's interpolated, but anyway) position. Since you know vectors, I'll start off with a basic collision test, a segment against a plane. Sorry, but you have to start there to understand time-based system. [grin]

have segment [OldLoc, NewLoc].

have a plane [O, N], where O (Origin) is an arbitrary point on a plane.

In your example, let the plane be with normal N(-1, 0), and with Origin being the top-left corner of the blue box (point (xmin, ymax) of the box).




A P point is on the plane [O, N] if it satisfies the equation

(P - O) . N = 0

P is on segment [OldPos, NewPos] if it satisfied the equation

P = OldPos + t * (NewPos - OldPos), and 0 <= t <= 1.

so, if the segment intersects the plane at point P, P will satisfy both equations.

combined the equaitons, and you have

((OldPos - O) + t * (NewPos - OldPos)) . N = 0

therefore, t = [(O - OldPos) . N] / [(NewPos - OldPos) . N]

now that you have a value for t, if t < 0, the segment is therefore 'behind' the plane. if t > 1, the segment is 'in front' of the plane. if 0 <= t <= 1, the segment crosses the plane, and the point P = OldPos + t * (NewPos - OldPos).

bingo, you have segment / plane intersection, with an arbitrary plane.

in this example, the normal is N(-1, 0), and Origin O is (xmin, ymax).

decompose the equation to find t with the dot products, and you get...

t = [OldPos.x - xmin] / [OldPos.x - NewPos.x].

Now, a box is basically an intersection of two slabs (in 2D).



So, you process the intersection for each slab. You will have an enter and exit point (both at time tenter, and texit) where the segments crosses the planes. Of course tenter is always < texit.

for example, consider the [xmin, xmax] slabs.

as explained above,

txmin = [OldPos.x - xmin] / [OldPos.x - NewPos.x]
txmax = [xmax - OldPos.x] / [NewPos.x - OldPos.x]

ok, re-arrange txmin, so it looks nice

txmin = [xmin - OldPos.x] / [NewPos.x - OldPos.x]
txmax = [xmax - OldPos.x] / [NewPos.x - OldPos.x]

here, txmin < txmax, so we have txenter = txmin, txexit = txmax



you do the same for the Y-slab

tymin = [ymin - OldPos.y] / [NewPos.y - OldPos.y]
tymax = [ymax - OldPos.y] / [NewPos.y - OldPos.y]





here however, tymax will be < tymin. so we have tyenter = tymax, tyexit = tymin.


ok, so now, how to tell of the segment intersected the box? It's quite simple. The segment will intersect the box if the intervals [txenter, tyexit] and [tyenter, tyexit] overlap.

rings a bell? It loosely connected to the SAT. It's again a matter of 1D interval intersections. Here is a picture worth 100 words.






first picture, you can see that intervals [txenter, txexit] and [tyenter, tyexit] do not overlap (txexit < tyenter). The segment misses the box entirely.

second picture, the intervals overlap. the segment does indeed intersects the box. and you can see that it will intersect the box at point t = txenter, and exit the box at point t = txexit.

OK, .. so now, we have a basic algo to find if a segment intersects a box or not.

in code, it's quite simple. What we are interested in, is the time of entering the box, and also, exiting (why not, hey :)). lets call then tfirst, tlast.

There is also another interval to consider, the segment range itself, [0.0f, 1.0f]. the segment will intersect the box if obviously the found interval [tfirst, tlast] intersects the interval [0.0f, 1.0f].

I've also added a safety checks, in case the segment is parallel to an axis. If that's the case, you make sure the segment is inside the slab it is parallel with.

bool SegmentSlabIntersect(float slabmin, float slabmax, float oldpos, float newpos, float& tenter, float& texit){    float displacement = (newpos - oldpos);     if (fabs(displacement) < 1.0E-8f)    {         return (oldpos >= slabmin && oldpos <= slabmax);    }       float tslabmin = (slabmin - oldpos) / (newpos - oldpos);    float tslabmax = (slabmax - oldpos) / (newpos - oldpos);    if(tslabmin < tslabmax)    {         tenter = tslabmin;         texit  = tslabmax;    }    else    {         tenter = tslabmax;         texit  = tslabmin;    }    return true;}bool SegmentBoxIntersect(Vector BoxMin, Vector BoxMax, Vector OldPos, Vector NewPos, float& tfirst, float& tlast){    // calculate intervals for both slabs    float txenter, txexit;    float tyenter, tyexit;    if (!SegmentSlabIntersect(BoxMin.x, BoxMax.x, OldPos.x, NewPos.x, txenter, txexit))        return false;    if (!SegmentSlabIntersect(BoxMin.y, BoxMax.y, OldPos.y, NewPos.y, tyenter, tyexit))        return false;    // slab intervals dont overlap    if (txenter > tyexit || tyenter > txexit)         return false;    // get the intersection between the intervals    tfirst = max(txenter, tyenter);    tlast  = min(txexit,  tyexit);    // now check it's within the segment    if (tfirst > 1.0f || tlast < 0.0f)         return false;    tfirst = max(tfirst, 0.0f);    tlast  = min(tlast , 1.0f);    return true;}


so there you go, you know when a segment intersects a box.

Now, how does that translates to a box-box collision.

well, you can imagine the segment as a travelling point, and the times of intersections as the times the point hits box and exits, like a bullet. Easy enough.

Now, you can also consider the point as a box of size 0, and the first example of a point hitting a plane.

if you inflate the point to a box of extents (1, 1) (from the centre point, the sides will be at distance 1), and at the same time, push the plane back by the same amount, the time the small box will hit pushed-plane will be the same as the time when the point hit plane. Because effectively, the distance box-pushedplane, and the distance point-plane remain the same.






Now, thinking the other way around, by shrinking a box to a point, and expanding the other box by the same amount, we end up doing a segment-large box intersection test.

it is *that* simple.

in code again. There is a slight change in terminology, using 'dispalcement', instead of 'oldpos-newpos', which I think you'll understand. And I use 'collision' instead of 'intersection' since it's a bit more representative.

bool PointSlabCollision(float slabmin, float slabmax, float point, float displacement, float& tenter, float& texit){    if (fabs(displacement) < 1.0E-8f)    {        return (point > slabmin && point < slabmax);    }    float tslabmin = (slabmin - point) / (displacement);    float tslabmax = (slabmax - point) / (displacement);    if(tslabmin < tslabmax)    {         tenter = tslabmin;         texit  = tslabmax;    }    else    {         tenter = tslabmax;         texit  = tslabmin;    }    return true;}bool PointBoxCollision(Vector BoxMin, Vector BoxMax, Vector Point, Vector Displacement, float& tfirst, float& tlast){    // calculate intervals for both slabs    float txenter, txexit;    float tyenter, tyexit;    if (!PointSlabCollision(BoxMin.x, BoxMax.x, Point.x, Displacement.x, txenter, txexit))         return false;    if (!PointSlabCollision(BoxMin.y, BoxMax.y, Point.y, Displacement.y, tyenter, tyexit))        return false;    // slab intervals dont overlap    if (txenter > tyexit || tyenter > txexit)         return false;    // get the intersection between the intervals    tfirst = max(txenter, tyenter);    tlast  = min(txexit,  tyexit);    // now check it's within the segment    if (tfirst > 1.0f || tlast < 0.0f)         return false;    tfirst = max(tfirst, 0.0f);    tlast  = min(tlast , 1.0f);    return true;}bool BoxBoxCollision(Vector Amin, Vector Amax, Vector Bmin, Vector Bmax, Vector BDisplacement, float& tfirst, float& tlast){    Vector Bextent = (Bmax - Bmin) * 0.5f;    Vector Bcentre = (Bmax + Bmin) * 0.5f;    Amin += Bextent;    Amax -= Bextent;       return PointBoxCollision(Amin, Amax, BCentre, BDisplacement, tfirst, tlast);}


So there you go. If the boxes originally intersect, BCentre will be inside [Amin, Amax], and then tfirst will become 0.0f.

If that happens, you can fall back to a static collision test.

Also, I haven't discussed the problems of normals at times of collision. But I'll discuss that later, once you've digested all that.

NOTE : The gfx are from Word6, you need a non-black background to see the text annotations (it's written in black with transparent background :/).

[Edited by - oliii on November 14, 2005 2:52:31 AM]

Everything is better with Metal.

OK, moving on :)

first off, I'd like to rw-write some of the logic.

bool PointSlabCollision(float slabmin, float slabmax, float point, float displacement, float& tfirst, float& tlast){    if (fabs(displacement) < 1.0E-8f)    {        return (point > slabmin && point < slabmax);    }    float tslabmin = (slabmin - point) / (displacement);    float tslabmax = (slabmax - point) / (displacement);    float tslabenter;    float tslabexit;    if(tslabmin < tslabmax)    {        tslabenter = tslabmin;        tslabexit  = tslabmax;    }    else    {        tslabenter = tslabmax;        tslabexit  = tslabmin;    }    tfirst = max(tfirst, tslabenter);    tlast  = min(tlast , tslabexit);    if (tfirst < tlast)        return false;    return true;}bool PointBoxCollision(Vector BoxMin, Vector BoxMax, Vector Point, Vector displacement, float& tfirst, float& tlast){    // calculate intervals for both slabs    if (!PointSlabCollision(BoxMin.x, BoxMax.x, Point.x, Displacement.x, tfirst, tlast))         return false;    if (!PointSlabCollision(BoxMin.y, BoxMax.y, Point.y, Displacement.y, tfirst, tlast))        return false;    return true;}bool BoxBoxCollision(Vector Amin, Vector Amax, Vector Bmin, Vector Bmax, Vector BDisplacement, float& tfirst, float& tlast){    tfirst = 0.0f;    tlast  = 1.0f;    Vector Bextent = (Bmax - Bmin) * 0.5f;    Vector Bcentre = (Bmax + Bmin) * 0.5f;    Amin += Bextent;    Amax -= Bextent;       return PointBoxCollision(Amin, Amax, BCentre, BDisplacement, tfirst, tlast);}



OK, now that the logic is sorted, we can think of adding collision normals.

this part really, is traight forward. There is little change to the code required.

bool PointSlabCollision(float slabmin, float slabmax, float point, float displacement, const Vector& SlabNormal, float& tfirst, Vector& Nfirst, float& tlast, Vector& Nlast){	if (fabs(displacement) < 1.0E-8f)	{		return (point > slabmin && point < slabmax);	}	float tslabmin = (slabmin - point) / (displacement);	float tslabmax = (slabmax - point) / (displacement);	float tslabenter;	float tslabexit;	float sign;	if(tslabmin < tslabmax)	{		tslabenter = tslabmin;		tslabexit  = tslabmax;		sign = -1.0f;	}	else	{		tslabenter = tslabmax;		tslabexit  = tslabmin;		sign = 1.0f;	}	if (tslabenter > tfirst)	{		tfirst = tslabenter;		Nfirst = SlabNormal * sign;	}	if (tslabexit < tlast)	{		tlast  = tslabexit;		Nlast  = SlabNormal * -sign;	}	if (tlast < tfirst)		return false;	return true;}bool PointBoxCollision(const Vector& BoxMin, const Vector& BoxMax, const Vector& Point, const Vector& Displacement, float& tfirst, Vector& Nfirst, float& tlast, Vector& Nlast){	// calculate intervals for both slabs	if (!PointSlabCollision(BoxMin.x, BoxMax.x, Point.x, Displacement.x, Vector(1, 0), tfirst, Nfirst, tlast, Nlast)) 		return false;	if (!PointSlabCollision(BoxMin.y, BoxMax.y, Point.y, Displacement.y, Vector(0, 1), tfirst, Nfirst, tlast, Nlast))		return false;	return true;}bool BoxBoxCollision(const Vector& Amin, const Vector& Amax, const Vector& Bmin, const Vector& Bmax, const Vector& Displacement, float& tfirst, Vector& Nfirst, float& tlast, Vector& Nlast){	tfirst = 0.0f;	tlast  = 1.0f;	Nfirst = Vector(0, 0);	Nlast = Vector(0, 0);	Vector Bextent = (Bmax - Bmin) * 0.5f;	Vector Bcentre = (Bmax + Bmin) * 0.5f;	return PointBoxCollision(Amin - Bextent, Amax + Bextent, Bcentre, Displacement, tfirst, Nfirst, tlast, Nlast);}


BoxBoxCollision2.cpp

OK, now I'll get back to the static test, and calculating the MTD vector for two intersecting rectangles.

first of, to detect if two boxes intersect, it is quite straight forward.

bool BoxBoxIntersect(Vector Amin, Vector Amax, Vector Bmin, Vector Bmax){     if (Amin.x > Bmax.x || Bmin.x > Amax.x)         return false;     if (Amin.y > Bmax.y || Bmin.y > Amax.y)         return false;    return true;}


BoxBoxCollision2.cpp


Nothing can be simpler. :)

Now, usually, when we have an intersection, we'd like to make the intersection dissappear, by moving one or both box away from each other. Thus, we need to find which direction and by what amount we need to push the boxes appart.




From the first picture, it is quite obvious that we'd like to move the green the box upwards until the boxes only touch. Moving the box to the left, right or down, would require a much bigger translation, which would most likely be pushing in the wrong direction.

So, we'd like to calculate that MTD vector, or aka the Minimum Translation Distance, or distance of minimum translation to make the box stop intersecting.

Calculating the MTD in case of two boxes is quite simple, and basically falls down to find the minimum amount of overlap between intervals of the boxes, along the X and Y axis.

Again, I will simplify the problem by converting a BoxBox Intersection by a PointBoxIntersection. Following the same logic as the swept test, you shrink a box to a point, while expand the other box by the same amount. Then the problem becomes finding the side of the box the closest to the point.


bool PointSlabsIntersect(float slabmin, float slabmax, float point, const Vector& SlabNormal, Vector& MTD, float& mtdsquared){    	float dist_min = slabmin - point;	float dist_max = slabmax - point;	// point outside the slab	if (dist_min > 0.0f || dist_max < 0.0f)		return false;	// the vector we use to push the point outside the slab	float slabmtd  = (fabs(dist_min) < fabs(dist_max))? dist_min : dist_max;	Vector SlabMTD = SlabNormal * slabmtd;	float slabmtdsquared = SlabMTD * SlabMTD;	// Check if that push vector is smaller than our curent push vector	if (slabmtdsquared < mtdsquared || mtdsquared < 0.0f)	{		MTD = SlabMTD;		mtdsquared = slabmtdsquared;	}	return true;}bool PointBoxIntersect(const Vector& Min, const Vector& Max, const Vector& Point, Vector& MTD){	float mtdsquared = -1.0f;	// Test X axis	if (!PointSlabsIntersect(Min.x, Max.x, Point.x, Vector(1, 0), MTD, mtdsquared))		return false; 	// Test Y axis	if (!PointSlabsIntersect(Min.y, Max.y, Point.y, Vector(0, 1), MTD, mtdsquared))		return false;	return true;}bool BoxBoxIntersect(const Vector& Amin, const Vector& Amax, const Vector& Bmin, const Vector& Bmax, Vector& MTD){	// clear MTD	MTD = Vector(0, 0);		// convert Box-Box problem into point box problem	Vector Bextents = (Bmax - Bmin) * 0.5f;	Vector Bcentre  = (Bmax + Bmin) * 0.5f;	return PointBoxIntersect(Amin - Bextents, Amax + Bextents, Bcentre, MTD);}


BoxBoxMTD.cpp

PointSlabIntersect might feel a bit winded, but it is a necessary evil for later on.

When we work on a slab, we first find if the point is inside the slab or not. If not, the point cannont be inside the box.

If the point is inside the slab, we need to find the minimum distance from the point to the slab planes, and push the point there. This is where SlabMTD comes from, and tells us which slab side is the closest to the point.

Now, we also neem to make sure that that SlabMTD is indeed the smallest vector we can find to push the point outside the box.

Instead of comparing vector length, I simply compare vector length squared.

Ultimately, we will end up with a MTD vector, that we can use to push the virtual point outside the large box, which in turn, can be used to push the two intersecting boxes apart.

I choose to use that representation so now, we can plug it into the swept test as a fallback procedure in case the swept boxes intersecting, or if the boxes are static.

[Edited by - oliii on October 13, 2005 5:02:40 PM]

Everything is better with Metal.

OK, now to finish it. We can combine both the time-based movement, and the intersection test, in one single algorithm.

bool PointSlabCollision(float slabmin, float slabmax, float point, float displacement, const Vector& SlabNormal, float& tfirst, Vector& Nfirst, float& tlast, Vector& Nlast, Vector& MTD, float& mtdsquared, bool& bCollision){	float dist_min = slabmin - point;	float dist_max = slabmax - point;	// point inside the slab	bool bIntersect = (dist_min < 0.0f && dist_max > 0.0f);	if (bIntersect)	{		// the vector we use to push the point outside the slab		float slabmtd  = (fabs(dist_min) < fabs(dist_max))? dist_min : dist_max;		Vector SlabMTD = SlabNormal * slabmtd;		float slabmtdsquared = SlabMTD * SlabMTD;		// Check if that push vector is smaller than our curent push vector		if (slabmtdsquared < mtdsquared || mtdsquared < 0.0f)		{			MTD = SlabMTD;			mtdsquared = slabmtdsquared;		}	}	// if not moving, can only collide	// through intersections	if (fabs(displacement) < 1.0E-8f)		return bIntersect;			float tslabmin = (slabmin - point) / (displacement);	float tslabmax = (slabmax - point) / (displacement);	float tslabenter;	float tslabexit;	float sign;	if(tslabmin < tslabmax)	{		tslabenter = tslabmin;		tslabexit  = tslabmax;		sign = -1.0f;	}	else	{		tslabenter = tslabmax;		tslabexit  = tslabmin;		sign = 1.0f;	}	if (tslabenter > tfirst)	{		tfirst = tslabenter;		Nfirst = SlabNormal * sign;		bCollision = true;	}	if (tslabexit < tlast)	{		tlast  = tslabexit;		Nlast  = SlabNormal * -sign;	}	if (tlast < tfirst)		return false;	return true;}bool PointBoxCollision(const Vector& BoxMin, const Vector& BoxMax, const Vector& Point, const Vector& Displacement, float& tfirst, Vector& Nfirst, float& tlast, Vector& Nlast, Vector& MTD, float& mtdsquared, bool& bCollision){	// calculate intervals for both slabs	if (!PointSlabCollision(BoxMin.x, BoxMax.x, Point.x, Displacement.x, Vector(1, 0), tfirst, Nfirst, tlast, Nlast, MTD, mtdsquared, bCollision)) 		return false;	if (!PointSlabCollision(BoxMin.y, BoxMax.y, Point.y, Displacement.y, Vector(0, 1), tfirst, Nfirst, tlast, Nlast, MTD, mtdsquared, bCollision))		return false;	return true;}bool BoxBoxCollision(const Vector& Amin, const Vector& Amax, const Vector& Bmin, const Vector& Bmax, const Vector& Displacement, float& tfirst, Vector& Nfirst, Vector& MTD){	const float tolerance = 1.0E-8f;	bool bCollision = false;	float mtdsquared = -1.0f;	MTD = Vector(0, 0);	tfirst = 0.0f - tolerance;	Nfirst = Vector(0, 0);		float tlast  = 1.0f + tolerance;	Vector Nlast = Vector(0, 0);	Vector Bextent = (Bmax - Bmin) * 0.5f;	Vector Bcentre = (Bmax + Bmin) * 0.5f;	bool bCollisionOrIntersection = (PointBoxCollision(Amin - Bextent, Amax + Bextent, Bcentre, Displacement, tfirst, Nfirst, tlast, Nlast, MTD, mtdsquared));	if(!bCollisionOrIntersection)	{		return false;	}	if (bCollision)	{		// check if we found a valid time of collision		if(tfirst < 0.0f || tfirst > 1.0f)			return false;		MTD = Vector(0, 0);	}	else	{		tfirst = 0.0f;		Nfirst = MTD;		Nfirst.Normalise();	}	return true;}


BoxBoxCollisionAndMTD.cpp

This combines both the static and time-based test in one function. There is nothing really particular about it, except for what decides if we have a collision, or an intersection. Quite simply, if the first time of collision is <= 0 (well, tfirst *will* be 0), then we are already intersecting, and the result reverts to returning the MTD. The collision normal is also set to be the MTD direction, as this will be the case in 99.99% of the times.

This algorithm can easily be extended to 3D boxes, but also 2D oriented boxes, 2D polygons, 3D polygons, 3D triangles, triangles against boxes, convex hulls, ... All you need is use the same basic underlying principles of the SAT, and apply the time-based modifications.

[Edited by - oliii on November 14, 2005 2:35:54 AM]

Everything is better with Metal.

This topic is closed to new replies.

Advertisement