Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

Intersection.h

Go to the documentation of this file.
00001 /************************************************************** ggt-head beg
00002  *
00003  * GGT: Generic Graphics Toolkit
00004  *
00005  * Original Authors:
00006  *   Allen Bierbaum
00007  *
00008  * -----------------------------------------------------------------
00009  * File:          $RCSfile: Intersection.h,v $
00010  * Date modified: $Date: 2003/03/17 00:10:31 $
00011  * Version:       $Revision: 1.10 $
00012  * -----------------------------------------------------------------
00013  *
00014  *********************************************************** ggt-head end */
00015 /*************************************************************** ggt-cpr beg
00016 *
00017 * GGT: The Generic Graphics Toolkit
00018 * Copyright (C) 2001,2002 Allen Bierbaum
00019 *
00020 * This library is free software; you can redistribute it and/or
00021 * modify it under the terms of the GNU Lesser General Public
00022 * License as published by the Free Software Foundation; either
00023 * version 2.1 of the License, or (at your option) any later version.
00024 *
00025 * This library is distributed in the hope that it will be useful,
00026 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00028 * Lesser General Public License for more details.
00029 *
00030 * You should have received a copy of the GNU Lesser General Public
00031 * License along with this library; if not, write to the Free Software
00032 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00033 *
00034  ************************************************************ ggt-cpr end */
00035 #ifndef _GMTL_INTERSECTION_H_
00036 #define _GMTL_INTERSECTION_H_
00037 
00038 #include <algorithm>
00039 #include <gmtl/AABox.h>
00040 #include <gmtl/Point.h>
00041 #include <gmtl/Sphere.h>
00042 #include <gmtl/Vec.h>
00043 #include <gmtl/Plane.h>
00044 #include <gmtl/VecOps.h>
00045 #include <gmtl/Math.h>
00046 #include <gmtl/Ray.h>
00047 #include <gmtl/LineSeg.h>
00048 #include <gmtl/Tri.h>
00049 
00050 namespace gmtl
00051 {
00061    template<class DATA_TYPE>
00062    bool intersect(const AABox<DATA_TYPE>& box1, const AABox<DATA_TYPE>& box2)
00063    {
00064       // Look for a separating axis on each box for each axis
00065       if (box1.getMin()[0] > box2.getMax()[0])  return false;
00066       if (box1.getMin()[1] > box2.getMax()[1])  return false;
00067       if (box1.getMin()[2] > box2.getMax()[2])  return false;
00068 
00069       if (box2.getMin()[0] > box1.getMax()[0])  return false;
00070       if (box2.getMin()[1] > box1.getMax()[1])  return false;
00071       if (box2.getMin()[2] > box1.getMax()[2])  return false;
00072 
00073       // No separating axis ... they must intersect
00074       return true;
00075    }
00076    
00086    template<class DATA_TYPE>
00087    bool intersect( const AABox<DATA_TYPE>& box, const Point<DATA_TYPE, 3>& point )
00088    {
00089       // Look for a separating axis on each box for each axis
00090       if (box.getMin()[0] > point[0])  return false;
00091       if (box.getMin()[1] > point[1])  return false;
00092       if (box.getMin()[2] > point[2])  return false;
00093 
00094       if (point[0] > box.getMax()[0])  return false;
00095       if (point[1] > box.getMax()[1])  return false;
00096       if (point[2] > box.getMax()[2])  return false;
00097 
00098       // they must intersect
00099       return true;
00100    }
00101 
00116    template<class DATA_TYPE>
00117    bool intersect( const AABox<DATA_TYPE>& box1, const Vec<DATA_TYPE, 3>& path1,
00118                    const AABox<DATA_TYPE>& box2, const Vec<DATA_TYPE, 3>& path2,
00119                    DATA_TYPE& firstContact, DATA_TYPE& secondContact )
00120    {
00121       // Algorithm taken from Gamasutra's article, "Simple Intersection Test for
00122       // Games" - http://www.gamasutra.com/features/19991018/Gomez_3.htm
00123       //
00124       // This algorithm is solved from the frame of reference of box1
00125 
00126       // Get the relative path (in normalized time)
00127       Vec<DATA_TYPE, 3> path = path2 - path1;
00128 
00129       // The first time of overlap along each axis
00130       Vec<DATA_TYPE, 3> overlap1(DATA_TYPE(0), DATA_TYPE(0), DATA_TYPE(0));
00131 
00132       // The second time of overlap along each axis
00133       Vec<DATA_TYPE, 3> overlap2(DATA_TYPE(1), DATA_TYPE(1), DATA_TYPE(1));
00134 
00135       // Check if the boxes already overlap
00136       if (intersect(box1, box2))
00137       {
00138          firstContact = secondContact = DATA_TYPE(0);
00139          return true;
00140       }
00141 
00142       // Find the possible first and last times of overlap along each axis
00143       for (int i=0; i<3; ++i)
00144       {
00145          if ((box1.getMax()[i] < box2.getMin()[i]) && (path[i] < DATA_TYPE(0)))
00146          {
00147             overlap1[i] = (box1.getMax()[i] - box2.getMin()[i]) / path[i];
00148          }
00149          else if ((box2.getMax()[i] < box1.getMin()[i]) && (path[i] > DATA_TYPE(0)))
00150          {
00151             overlap1[i] = (box1.getMin()[i] - box2.getMax()[i]) / path[i];
00152          }
00153 
00154          if ((box2.getMax()[i] > box1.getMin()[i]) && (path[i] < DATA_TYPE(0)))
00155          {
00156             overlap2[i] = (box1.getMin()[i] - box2.getMax()[i]) / path[i];
00157          }
00158          else if ((box1.getMax()[i] > box2.getMin()[i]) && (path[i] > DATA_TYPE(0)))
00159          {
00160             overlap2[i] = (box1.getMax()[i] - box2.getMin()[i]) / path[i];
00161          }
00162       }
00163 
00164       // Calculate the first time of overlap
00165       firstContact = Math::Max(overlap1[0], overlap1[1], overlap1[2]);
00166 
00167       // Calculate the second time of overlap
00168       secondContact = Math::Min(overlap2[0], overlap2[1], overlap2[2]);
00169 
00170       // There could only have been a collision if the first overlap time
00171       // occurred before the second overlap time
00172       return firstContact <= secondContact;
00173    }
00174 
00189    template<class DATA_TYPE>
00190    bool intersect(const Sphere<DATA_TYPE>& sph1, const Vec<DATA_TYPE, 3>& path1,
00191                   const Sphere<DATA_TYPE>& sph2, const Vec<DATA_TYPE, 3>& path2,
00192                   DATA_TYPE& firstContact, DATA_TYPE& secondContact)
00193    {
00194       // Algorithm taken from Gamasutra's article, "Simple Intersection Test for
00195       // Games" - http://www.gamasutra.com/features/19991018/Gomez_2.htm
00196       //
00197       // This algorithm is solved from the frame of reference of sph1
00198 
00199       // Get the relative path (in normalized time)
00200       const Vec<DATA_TYPE, 3> path = path2 - path1;
00201 
00202       // Get the vector from sph1's starting point to sph2's starting point
00203       const Vec<DATA_TYPE, 3> start_offset = sph2.getCenter() - sph1.getCenter();
00204 
00205       // Compute the sum of the radii
00206       const DATA_TYPE radius_sum = sph1.getRadius() + sph2.getRadius();
00207 
00208       // u*u coefficient
00209       const DATA_TYPE a = dot(path, path);
00210 
00211       // u coefficient
00212       const DATA_TYPE b = DATA_TYPE(2) * dot(path, start_offset);
00213 
00214       // constant term
00215       const DATA_TYPE c = dot(start_offset, start_offset) - radius_sum * radius_sum;
00216 
00217       // Check if they're already overlapping
00218       if (dot(start_offset, start_offset) <= radius_sum * radius_sum)
00219       {
00220          firstContact = secondContact = DATA_TYPE(0);
00221          return true;
00222       }
00223 
00224       // Find the first and last points of intersection
00225       if (Math::quadraticFormula(firstContact, secondContact, a, b, c))
00226       {
00227          // Swap first and second contacts if necessary
00228          if (firstContact > secondContact)
00229          {
00230             std::swap(firstContact, secondContact);
00231             return true;
00232          }
00233       }
00234 
00235       return false;
00236    }
00237 
00247    template<class DATA_TYPE>
00248    bool intersect(const AABox<DATA_TYPE>& box, const Sphere<DATA_TYPE>& sph)
00249    {
00250       DATA_TYPE dist_sqr = DATA_TYPE(0);
00251 
00252       // Compute the square of the distance from the sphere to the box
00253       for (int i=0; i<3; ++i)
00254       {
00255          if (sph.getCenter()[i] < box.getMin()[i])
00256          {
00257             DATA_TYPE s = sph.getCenter()[i] - box.getMin()[i];
00258             dist_sqr += s*s;
00259          }
00260          else if (sph.getCenter()[i] > box.getMax()[i])
00261          {
00262             DATA_TYPE s = sph.getCenter()[i] - box.getMax()[i];
00263             dist_sqr += s*s;
00264          }
00265       }
00266 
00267       return dist_sqr <= (sph.getRadius()*sph.getRadius());
00268    }
00269 
00279    template<class DATA_TYPE>
00280    bool intersect(const Sphere<DATA_TYPE>& sph, const AABox<DATA_TYPE>& box)
00281    {
00282       return intersect(box, sph);
00283    }
00284 
00291    template<class DATA_TYPE>
00292    bool intersect( const Sphere<DATA_TYPE>& sphere, const Point<DATA_TYPE, 3>& point )
00293    {
00294       gmtl::Vec<DATA_TYPE, 3> offset = point - sphere.getCenter();
00295       DATA_TYPE dist = lengthSquared( offset ) - sphere.getRadius() * sphere.getRadius();
00296       
00297       // point is inside the sphere when true
00298       return  dist <= 0;
00299    }
00300 
00310    template<typename T>
00311    inline bool intersect( const Sphere<T>& sphere, const Ray<T>& ray, int& numhits, float& t0, float& t1 )
00312    {
00313       numhits = -1;
00314 
00315       // set up quadratic Q(t) = a*t^2 + 2*b*t + c
00316       Vec<T, 3> offset = ray.getOrigin() - sphere.getCenter();
00317       T a = lengthSquared( ray.getDir() );
00318       T b = dot( offset, ray.getDir() );
00319       T c = lengthSquared( offset ) - sphere.getRadius() * sphere.getRadius();
00320 
00321       // no intersection if Q(t) has no real roots
00322       T discriminant = b * b - a * c;
00323       if (discriminant < 0.0f)
00324       {
00325          numhits = 0;
00326          return false;
00327       }
00328       else if (discriminant > 0.0f)
00329       {
00330          T root = Math::sqrt( discriminant );
00331          T invA = T(1) / a;
00332          t0 = (-b - root) * invA;
00333          t1 = (-b + root) * invA;
00334 
00335          // assert: t0 < t1 since A > 0
00336 
00337          if (t0 >= T(0))
00338          {
00339             numhits = 2;
00340             return true;
00341          }
00342          else if (t1 >= 0.0f)
00343          {
00344             numhits = 1;
00345             t0 = t1;
00346             return true;
00347          }
00348          else
00349          {
00350             numhits = 0; 
00351             return false;
00352          }
00353       }
00354       else
00355       {
00356          t0 = -b / a;
00357          if (t0 >= T(0))
00358          {
00359             numhits = 1;
00360             return true;
00361          }
00362          else
00363          {
00364             numhits = 0;
00365             return false;
00366          }
00367       }
00368    }
00369 
00370    // intersect LineSeg/Sphere.
00371    // does intersection on sphere surface, point inside sphere doesn't count as an intersection
00372    // returns intersection point(s) in t
00373    // find intersection point(s) with: ray.getOrigin() + ray.getDir() * t
00374    // numhits, t0, t1 are undefined if return value is false
00375    template<typename T>
00376    inline bool intersect( const Sphere<T>& sphere, const LineSeg<T>& lineseg, int& numhits, float& t0, float& t1 )
00377    {
00378       if (intersect( sphere, Ray<T>( lineseg ), numhits, t0, t1 ))
00379       {
00380          // throw out hits that are past 1 in segspace (off the end of the lineseg)
00381          while (0 < numhits && 1.0f < t0) 
00382          {
00383             --numhits;
00384             t0 = t1;
00385          }
00386          if (2 == numhits && 1.0f < t1)
00387          {
00388             --numhits;
00389          }
00390          return 0 < numhits;
00391       }
00392       else
00393       {
00394          return false;
00395       }
00396    }
00397    
00408    template<class DATA_TYPE>
00409    bool intersect( const Plane<DATA_TYPE>& plane, const Ray<DATA_TYPE>& ray, DATA_TYPE& t )
00410    {
00411       Vec<DATA_TYPE, 3> N( plane.getNormal() );
00412       t = dot( N, N * plane.getOffset() - ray.getOrigin() ) / dot( N, ray.getDir() );
00413       return (DATA_TYPE)0 <= t && t <= (DATA_TYPE)1.0; 
00414    }
00415 
00428    template<class DATA_TYPE>
00429    bool intersect( const Tri<DATA_TYPE>& tri, const Ray<DATA_TYPE>& ray, 
00430                         float& u, float& v, float& t )
00431    {
00432       const float EPSILON = (DATA_TYPE)0.00001;
00433       Vec<DATA_TYPE, 3> edge1, edge2, tvec, pvec, qvec;
00434       float det,inv_det;
00435 
00436       /* find vectors for two edges sharing vert0 */
00437       edge1 = tri[1] - tri[0];
00438       edge2 = tri[2] - tri[0];
00439 
00440       /* begin calculating determinant - also used to calculate U parameter */
00441       gmtl::cross( pvec, ray.getDir(), edge2 );
00442 
00443       /* if determinant is near zero, ray lies in plane of triangle */
00444       det = gmtl::dot( edge1, pvec );
00445 
00446       if (det < EPSILON)
00447          return false;
00448 
00449       /* calculate distance from vert0 to ray origin */
00450       tvec = ray.getOrigin() - tri[0];
00451 
00452       /* calculate U parameter and test bounds */
00453       u = gmtl::dot( tvec, pvec );
00454       if (u < 0.0 || u > det)
00455          return false;
00456 
00457       /* prepare to test V parameter */
00458       gmtl::cross( qvec, tvec, edge1 );
00459 
00460       /* calculate V parameter and test bounds */
00461       v = gmtl::dot( ray.getDir(), qvec );
00462       if (v < 0.0 || u + v > det)
00463          return false;
00464 
00465       /* calculate t, scale parameters, ray intersects triangle */
00466       t = gmtl::dot( edge2, qvec );
00467       inv_det = ((DATA_TYPE)1.0) / det;
00468       t *= inv_det;
00469       u *= inv_det;
00470       v *= inv_det;
00471       
00472       // test if t is within the ray boundary (when t >= 0)
00473       return t >= (DATA_TYPE)0;
00474    }
00475 
00488    template<class DATA_TYPE>
00489    bool intersect( const Tri<DATA_TYPE>& tri, const LineSeg<DATA_TYPE>& lineseg, 
00490                         float& u, float& v, float& t )
00491    {
00492       const DATA_TYPE eps = (DATA_TYPE)0.0001010101;
00493       DATA_TYPE l = length( lineseg.getDir() );
00494       if (eps < l)
00495       {
00496          Ray<DATA_TYPE> temp( lineseg.getOrigin(), lineseg.getDir() / l );
00497          bool result = intersect( tri, temp, u, v, t );
00498          t /= lineseg.getLength(); // need to normalize the result
00499          return result && t <= (DATA_TYPE)1.0;
00500       }
00501       else 
00502          return false;
00503    }
00504 }
00505 
00506    
00507 
00508 #endif

Generated on Mon Apr 7 15:28:54 2003 for GenericMathTemplateLibrary by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002