/// <summary>Determines the time when the box will hit another box</summary>
/// <param name="firstBox">First box that will be checked for impact</param>
/// <param name="secondBox">Second box that will be checked for impact</param>
/// <param name="firstVelocity">Velocity with which the first box is moving</param>
/// <returns>The point of first contact, if any</returns>
/// <remarks>
/// <para>
/// Conventional tests that resort to stepping often fail to detect collisions
/// between fast-moving objects. This impact determination test will always
/// detect a collision if it occurs, giving the exact time of the impact.
/// </para>
/// <para>
/// This is a simplified test that assumes a linear trajectory and does
/// not take object rotation into account. It is well suited to use on
/// two bounding boxes in order to determine if a collision between the
/// shapes contained is possible at all.
/// </para>
/// <para>
/// Idea taken from the "Simple Intersection Tests for Games" article
/// on gamasutra by Gomez.
/// (http://www.gamasutra.com/features/19991018/Gomez_1.htm)
/// </para>
/// </remarks>
internal static float? FindContact(
AxisAlignedBox3 firstBox, Vector3 firstVelocity,
AxisAlignedBox3 secondBox
) {
Vector3 firstContact = new Vector3(0.0f, 0.0f, 0.0f);
Vector3 lastContact = new Vector3(1.0f, 1.0f, 1.0f);
// This could be done in a loop if the Vector3 class provided an indexing
// operator for accessing the vector components. Sadly, as of XNA 1.1,
// this is not the case, so that leaves us with this mess :)
// X axis
if(secondBox.Max.X < firstBox.Min.X && firstVelocity.X < 0.0f)
firstContact.X = (secondBox.Max.X - firstBox.Min.X) / firstVelocity.X;
else if(firstBox.Max.X < secondBox.Min.X && firstVelocity.X > 0.0f)
firstContact.X = (secondBox.Min.X - firstBox.Max.X) / firstVelocity.X;
if(firstBox.Max.X > secondBox.Min.X && firstVelocity.X < 0.0f)
lastContact.X = (secondBox.Min.X - firstBox.Max.X) / firstVelocity.X;
else if(secondBox.Max.X > firstBox.Min.X && firstVelocity.X > 0.0f)
lastContact.X = (secondBox.Max.X - firstBox.Min.X) / firstVelocity.X;
// Y axis
if(secondBox.Max.Y < firstBox.Min.Y && firstVelocity.Y < 0.0f)
firstContact.Y = (secondBox.Max.Y - firstBox.Min.Y) / firstVelocity.Y;
else if(firstBox.Max.Y < secondBox.Min.Y && firstVelocity.Y > 0.0f)
firstContact.Y = (secondBox.Min.Y - firstBox.Max.Y) / firstVelocity.Y;
if(firstBox.Max.Y > secondBox.Min.Y && firstVelocity.Y < 0.0f)
lastContact.Y = (secondBox.Min.Y - firstBox.Max.Y) / firstVelocity.Y;
else if(secondBox.Max.Y > firstBox.Min.Y && firstVelocity.Y > 0.0f)
lastContact.Y = (secondBox.Max.Y - firstBox.Min.Y) / firstVelocity.Y;
// Z axis
if(secondBox.Max.Z < firstBox.Min.Z && firstVelocity.Z < 0.0f)
firstContact.Z = (secondBox.Max.Z - firstBox.Min.Z) / firstVelocity.Z;
else if(firstBox.Max.Z < secondBox.Min.Z && firstVelocity.Z > 0.0f)
firstContact.Z = (secondBox.Min.Z - firstBox.Max.Z) / firstVelocity.Z;
if(firstBox.Max.Z > secondBox.Min.Z && firstVelocity.Z < 0.0f)
lastContact.Z = (secondBox.Min.Z - firstBox.Max.Z) / firstVelocity.Z;
else if(secondBox.Max.Z > firstBox.Min.Z && firstVelocity.Z > 0.0f)
lastContact.Z = (secondBox.Max.Z - firstBox.Min.Z) / firstVelocity.Z;
// We now extract the exact time of the box' entry into the other box
// as well as the time of exit (if any)
float entry = Math.Max(
firstContact.X, Math.Max(firstContact.Y, firstContact.Z)
);
float exit = Math.Min(
lastContact.X, Math.Min(lastContact.Y, lastContact.Z)
);
if(entry > exit)
return null;
return entry;
}
/// <param name="firstBox">First box that will be checked for impact</param>
/// <param name="secondBox">Second box that will be checked for impact</param>
/// <param name="firstVelocity">Velocity with which the first box is moving</param>
/// <returns>The point of first contact, if any</returns>
/// <remarks>
/// <para>
/// Conventional tests that resort to stepping often fail to detect collisions
/// between fast-moving objects. This impact determination test will always
/// detect a collision if it occurs, giving the exact time of the impact.
/// </para>
/// <para>
/// This is a simplified test that assumes a linear trajectory and does
/// not take object rotation into account. It is well suited to use on
/// two bounding boxes in order to determine if a collision between the
/// shapes contained is possible at all.
/// </para>
/// <para>
/// Idea taken from the "Simple Intersection Tests for Games" article
/// on gamasutra by Gomez.
/// (http://www.gamasutra.com/features/19991018/Gomez_1.htm)
/// </para>
/// </remarks>
internal static float? FindContact(
AxisAlignedBox3 firstBox, Vector3 firstVelocity,
AxisAlignedBox3 secondBox
) {
Vector3 firstContact = new Vector3(0.0f, 0.0f, 0.0f);
Vector3 lastContact = new Vector3(1.0f, 1.0f, 1.0f);
// This could be done in a loop if the Vector3 class provided an indexing
// operator for accessing the vector components. Sadly, as of XNA 1.1,
// this is not the case, so that leaves us with this mess :)
// X axis
if(secondBox.Max.X < firstBox.Min.X && firstVelocity.X < 0.0f)
firstContact.X = (secondBox.Max.X - firstBox.Min.X) / firstVelocity.X;
else if(firstBox.Max.X < secondBox.Min.X && firstVelocity.X > 0.0f)
firstContact.X = (secondBox.Min.X - firstBox.Max.X) / firstVelocity.X;
if(firstBox.Max.X > secondBox.Min.X && firstVelocity.X < 0.0f)
lastContact.X = (secondBox.Min.X - firstBox.Max.X) / firstVelocity.X;
else if(secondBox.Max.X > firstBox.Min.X && firstVelocity.X > 0.0f)
lastContact.X = (secondBox.Max.X - firstBox.Min.X) / firstVelocity.X;
// Y axis
if(secondBox.Max.Y < firstBox.Min.Y && firstVelocity.Y < 0.0f)
firstContact.Y = (secondBox.Max.Y - firstBox.Min.Y) / firstVelocity.Y;
else if(firstBox.Max.Y < secondBox.Min.Y && firstVelocity.Y > 0.0f)
firstContact.Y = (secondBox.Min.Y - firstBox.Max.Y) / firstVelocity.Y;
if(firstBox.Max.Y > secondBox.Min.Y && firstVelocity.Y < 0.0f)
lastContact.Y = (secondBox.Min.Y - firstBox.Max.Y) / firstVelocity.Y;
else if(secondBox.Max.Y > firstBox.Min.Y && firstVelocity.Y > 0.0f)
lastContact.Y = (secondBox.Max.Y - firstBox.Min.Y) / firstVelocity.Y;
// Z axis
if(secondBox.Max.Z < firstBox.Min.Z && firstVelocity.Z < 0.0f)
firstContact.Z = (secondBox.Max.Z - firstBox.Min.Z) / firstVelocity.Z;
else if(firstBox.Max.Z < secondBox.Min.Z && firstVelocity.Z > 0.0f)
firstContact.Z = (secondBox.Min.Z - firstBox.Max.Z) / firstVelocity.Z;
if(firstBox.Max.Z > secondBox.Min.Z && firstVelocity.Z < 0.0f)
lastContact.Z = (secondBox.Min.Z - firstBox.Max.Z) / firstVelocity.Z;
else if(secondBox.Max.Z > firstBox.Min.Z && firstVelocity.Z > 0.0f)
lastContact.Z = (secondBox.Max.Z - firstBox.Min.Z) / firstVelocity.Z;
// We now extract the exact time of the box' entry into the other box
// as well as the time of exit (if any)
float entry = Math.Max(
firstContact.X, Math.Max(firstContact.Y, firstContact.Z)
);
float exit = Math.Min(
lastContact.X, Math.Min(lastContact.Y, lastContact.Z)
);
if(entry > exit)
return null;
return entry;
}