#region CPL License /* Nuclex Framework Copyright (C) 2002-2009 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using Microsoft.Xna.Framework; namespace Nuclex.Geometry.Volumes.Collisions { /// Contains all Aabb-to-Aabb interference detection code public static class AabbAabbCollider { /// Test whether two axis aligned boxes are overlapping /// Minimum coordinate of first box /// Maximum coordinate of first box /// Minimum coordinate of second box /// Maximum coordinate of second box /// True if the boxes are intersecting each other public static bool CheckContact( Vector3 firstMin, Vector3 firstMax, Vector3 secondMin, Vector3 secondMax ) { return (firstMin.X < secondMax.X) && (firstMax.X > secondMin.X) && (firstMin.Y < secondMax.Y) && (firstMax.Y > secondMin.Y) && (firstMin.Z < secondMax.Z) && (firstMax.Z > secondMin.Z); } /// Find the contact location between two axis aligned boxes /// Minimum coordinate of first box /// Maximum coordinate of first box /// Minimum coordinate of second box /// Maximum coordinate of second box /// A contact location if the boxes touch each other public static Vector3? FindContact( Vector3 firstMin, Vector3 firstMax, Vector3 secondMin, Vector3 secondMax ) { // Extract the intersecting area of the two boxes Vector3 min = Vector3.Max(firstMin, secondMin); Vector3 max = Vector3.Min(firstMax, secondMax); // If the boxes don't touch we don't have an intersection if((max.X < min.X) || (max.Y < min.Y) || (max.Z < min.Z)) return null; // The contact point is in the center of the intersecting area return (min + max) / 2.0f; } /// Determines the time when the box will hit another box /// Minimum coordinate of first box /// Maximum coordinate of first box /// Minimum coordinate of second box /// Maximum coordinate of second box /// /// Velocity with which the second box is moving relative to the first box /// /// The point of first contact, if any /// /// /// 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. /// /// /// 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. /// /// /// Idea taken from the "Simple Intersection Tests for Games" article /// on gamasutra by Gomez. /// (http://www.gamasutra.com/features/19991018/Gomez_1.htm) /// /// internal static float? FindContact( Vector3 firstMin, Vector3 firstMax, Vector3 secondMin, Vector3 secondMax, Vector3 secondVelocity ) { Vector3 secondContact = 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(firstMax.X < secondMin.X && secondVelocity.X < 0.0f) secondContact.X = (firstMax.X - secondMin.X) / secondVelocity.X; else if(secondMax.X < firstMin.X && secondVelocity.X > 0.0f) secondContact.X = (firstMin.X - secondMax.X) / secondVelocity.X; if(secondMax.X > firstMin.X && secondVelocity.X < 0.0f) lastContact.X = (firstMin.X - secondMax.X) / secondVelocity.X; else if(firstMax.X > secondMin.X && secondVelocity.X > 0.0f) lastContact.X = (firstMax.X - secondMin.X) / secondVelocity.X; // Y axis if(firstMax.Y < secondMin.Y && secondVelocity.Y < 0.0f) secondContact.Y = (firstMax.Y - secondMin.Y) / secondVelocity.Y; else if(secondMax.Y < firstMin.Y && secondVelocity.Y > 0.0f) secondContact.Y = (firstMin.Y - secondMax.Y) / secondVelocity.Y; if(secondMax.Y > firstMin.Y && secondVelocity.Y < 0.0f) lastContact.Y = (firstMin.Y - secondMax.Y) / secondVelocity.Y; else if(firstMax.Y > secondMin.Y && secondVelocity.Y > 0.0f) lastContact.Y = (firstMax.Y - secondMin.Y) / secondVelocity.Y; // Z axis if(firstMax.Z < secondMin.Z && secondVelocity.Z < 0.0f) secondContact.Z = (firstMax.Z - secondMin.Z) / secondVelocity.Z; else if(secondMax.Z < firstMin.Z && secondVelocity.Z > 0.0f) secondContact.Z = (firstMin.Z - secondMax.Z) / secondVelocity.Z; if(secondMax.Z > firstMin.Z && secondVelocity.Z < 0.0f) lastContact.Z = (firstMin.Z - secondMax.Z) / secondVelocity.Z; else if(firstMax.Z > secondMin.Z && secondVelocity.Z > 0.0f) lastContact.Z = (firstMax.Z - secondMin.Z) / secondVelocity.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(secondContact.X, Math.Max(secondContact.Y, secondContact.Z)); float exit = Math.Min(lastContact.X, Math.Min(lastContact.Y, lastContact.Z)); if(entry > exit) return null; return entry; } } } // namespace Nuclex.Geometry.Volumes.Collisions