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

OgreMath.cpp

Go to the documentation of this file.
00001 /*
00002 -----------------------------------------------------------------------------
00003 This source file is part of OGRE
00004     (Object-oriented Graphics Rendering Engine)
00005 For the latest info, see http://www.ogre3d.org/
00006 
00007 Copyright © 2000-2002 The OGRE Team
00008 Also see acknowledgements in Readme.html
00009 
00010 This program is free software; you can redistribute it and/or modify it under
00011 the terms of the GNU Lesser General Public License as published by the Free Software
00012 Foundation; either version 2 of the License, or (at your option) any later
00013 version.
00014 
00015 This program is distributed in the hope that it will be useful, but WITHOUT
00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
00018 
00019 You should have received a copy of the GNU Lesser General Public License along with
00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
00022 http://www.gnu.org/copyleft/lesser.txt.
00023 -----------------------------------------------------------------------------
00024 */
00025 #include "OgreStableHeaders.h"
00026 
00027 #include "OgreMath.h"
00028 #include "asm_math.h"
00029 #include "OgreVector3.h"
00030 #include "OgreRay.h"
00031 #include "OgreSphere.h"
00032 #include "OgreAxisAlignedBox.h"
00033 #include "OgrePlane.h"
00034 
00035 
00036 namespace Ogre
00037 {
00038 
00039     const Real Math::POS_INFINITY = std::numeric_limits<Real>::infinity();
00040     const Real Math::NEG_INFINITY = -std::numeric_limits<Real>::infinity();
00041     const Real Math::PI = Real( 4.0 * atan( 1.0 ) );
00042     const Real Math::TWO_PI = Real( 2.0 * PI );
00043     const Real Math::HALF_PI = Real( 0.5 * PI );
00044     const Real Math::fDeg2Rad = PI / Real(180.0);
00045     const Real Math::fRad2Deg = Real(180.0) / PI;
00046 
00047     int Math::mTrigTableSize;
00048    Math::AngleUnit Math::msAngleUnit;
00049 
00050     Real  Math::mTrigTableFactor;
00051     Real *Math::mSinTable = NULL;
00052     Real *Math::mTanTable = NULL;
00053 
00054     //-----------------------------------------------------------------------
00055     Math::Math( unsigned int trigTableSize )
00056     {
00057         msAngleUnit = AU_DEGREE;
00058 
00059         mTrigTableSize = trigTableSize;
00060         mTrigTableFactor = mTrigTableSize / Math::TWO_PI;
00061 
00062         mSinTable = new Real[mTrigTableSize];
00063         mTanTable = new Real[mTrigTableSize];
00064 
00065         buildTrigTables();
00066 
00067         // Init random number generator
00068         srand( (unsigned)time(0) );
00069     }
00070 
00071     //-----------------------------------------------------------------------
00072     Math::~Math()
00073     {
00074         delete [] mSinTable;
00075         delete [] mTanTable;
00076     }
00077 
00078     //-----------------------------------------------------------------------
00079     void Math::buildTrigTables(void)
00080     {
00081         // Build trig lookup tables
00082         // Could get away with building only PI sized Sin table but simpler this 
00083         // way. Who cares, it'll ony use an extra 8k of memory anyway and I like 
00084         // simplicity.
00085         Real angle;
00086         for (int i = 0; i < mTrigTableSize; ++i)
00087         {
00088             angle = Math::TWO_PI * i / mTrigTableSize;
00089             mSinTable[i] = sin(angle);
00090             mTanTable[i] = tan(angle);
00091         }
00092     }
00093     //-----------------------------------------------------------------------   
00094     Real Math::SinTable (Real fValue)
00095     {
00096         // Convert range to index values, wrap if required
00097         int idx;
00098         if (fValue >= 0)
00099         {
00100             idx = int(fValue * mTrigTableFactor) % mTrigTableSize;
00101         }
00102         else
00103         {
00104             idx = mTrigTableSize - (int(-fValue * mTrigTableFactor) % mTrigTableSize) - 1;
00105         }
00106 
00107         return mSinTable[idx];
00108     }
00109     //-----------------------------------------------------------------------
00110     Real Math::TanTable (Real fValue)
00111     {
00112         // Convert range to index values, wrap if required
00113         int idx = int(fValue *= mTrigTableFactor) % mTrigTableSize;
00114         return mTanTable[idx];
00115     }
00116     //-----------------------------------------------------------------------
00117     int Math::ISign (int iValue)
00118     {
00119         return ( iValue > 0 ? +1 : ( iValue < 0 ? -1 : 0 ) );
00120     }
00121     //-----------------------------------------------------------------------
00122     Real Math::ACos (Real fValue)
00123     {
00124         if ( -1.0 < fValue )
00125         {
00126             if ( fValue < 1.0 )
00127                 return Real(acos(fValue));
00128             else
00129                 return 0.0;
00130         }
00131         else
00132         {
00133             return PI;
00134         }
00135     }
00136     //-----------------------------------------------------------------------
00137     Real Math::ASin (Real fValue)
00138     {
00139         if ( -1.0 < fValue )
00140         {
00141             if ( fValue < 1.0 )
00142                 return Real(asin(fValue));
00143             else
00144                 return -HALF_PI;
00145         }
00146         else
00147         {
00148             return HALF_PI;
00149         }
00150     }
00151     //-----------------------------------------------------------------------
00152     Real Math::Sign (Real fValue)
00153     {
00154         if ( fValue > 0.0 )
00155             return 1.0;
00156 
00157         if ( fValue < 0.0 )
00158             return -1.0;
00159 
00160         return 0.0;
00161     }
00162     //-----------------------------------------------------------------------
00163     Real Math::InvSqrt(Real fValue)
00164     {
00165         return Real(asm_rsq(fValue));
00166     }
00167     //-----------------------------------------------------------------------
00168     Real Math::UnitRandom ()
00169     {
00170         return asm_rand() / asm_rand_max();
00171     }
00172     
00173     //-----------------------------------------------------------------------
00174     Real Math::RangeRandom (Real fLow, Real fHigh)
00175     {
00176         return (fHigh-fLow)*UnitRandom() + fLow;
00177     }
00178 
00179     //-----------------------------------------------------------------------
00180     Real Math::SymmetricRandom ()
00181     {
00182         return 2.0f * UnitRandom() - 1.0f;
00183     }
00184 
00185    //-----------------------------------------------------------------------
00186     void Math::setAngleUnit(Math::AngleUnit unit)
00187    {
00188        msAngleUnit = unit;
00189    }
00190    //-----------------------------------------------------------------------
00191    Math::AngleUnit Math::getAngleUnit(void)
00192    {
00193        return msAngleUnit;
00194    }
00195     //-----------------------------------------------------------------------
00196     Real Math::AngleUnitsToRadians(Real angleunits)
00197     {
00198        if (msAngleUnit == AU_DEGREE)
00199            return angleunits * fDeg2Rad;
00200        else
00201            return angleunits;
00202     }
00203 
00204     //-----------------------------------------------------------------------
00205     Real Math::RadiansToAngleUnits(Real radians)
00206     {
00207        if (msAngleUnit == AU_DEGREE)
00208            return radians * fRad2Deg;
00209        else
00210            return radians;
00211     }
00212 
00213     //-----------------------------------------------------------------------
00214     bool Math::pointInTri2D( Real px, Real py, Real ax, Real ay, Real bx, Real by, Real cx, Real cy )
00215     {
00216         Real v1x, v2x, v1y, v2y;
00217         bool bClockwise;
00218 
00219         v1x = bx - ax;
00220         v1y = by - ay;
00221 
00222         v2x = px - bx;
00223         v2y = py - by;
00224 
00225         // For the sake of readability
00226         #define Clockwise ( v1x * v2y - v1y * v2x >= 0.0 )
00227 
00228         bClockwise = Clockwise;
00229 
00230         v1x = cx - bx;
00231         v1y = cy - by;
00232 
00233         v2x = px - cx;
00234         v2y = py - cy;
00235 
00236         if( Clockwise != bClockwise )
00237             return false;
00238 
00239         v1x = ax - cx;
00240         v1y = ay - cy;
00241 
00242         v2x = px - ax;
00243         v2y = py - ay;
00244 
00245         if( Clockwise != bClockwise )
00246             return false;
00247 
00248         return true;
00249 
00250         // Clean up the #defines
00251         #undef Clockwise
00252     }
00253 
00254     //-----------------------------------------------------------------------
00255     bool Math::RealEqual( Real a, Real b, Real tolerance )
00256     {
00257         if ((b < (a + tolerance)) && (b > (a - tolerance)))
00258             return true;
00259         else
00260             return false;
00261     }
00262 
00263     //-----------------------------------------------------------------------
00264     std::pair<bool, Real> Math::intersects(const Ray& ray, const Plane& plane)
00265     {
00266 
00267         Real denom = plane.normal.dotProduct(ray.getDirection());
00268         if (Math::Abs(denom) < std::numeric_limits<Real>::epsilon())
00269         {
00270             // Parallel
00271             return std::pair<bool, Real>(false, 0);
00272         }
00273         else
00274         {
00275             Real nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d;
00276             Real t = -(nom/denom);
00277             return std::pair<bool, Real>(t >= 0, t);
00278         }
00279         
00280     }
00281     //-----------------------------------------------------------------------
00282     std::pair<bool, Real> Math::intersects(const Ray& ray, const Sphere& sphere)
00283     {
00284         const Vector3& raydir = ray.getDirection();
00285         // Adjust ray origin relative to sphere center
00286         const Vector3& rayorig = ray.getOrigin() - sphere.getCenter();
00287         Real radius = sphere.getRadius();
00288 
00289         // Check origin inside first
00290         if (rayorig.squaredLength() <= radius*radius)
00291         {
00292             return std::pair<bool, Real>(true, 0);
00293         }
00294 
00295         // Mmm, quadratics
00296         // Build coeffs which can be used with std quadratic solver
00297         // ie t = (-b +/- sqrt(b*b + 4ac)) / 2a
00298         Real a = raydir.dotProduct(raydir);
00299         Real b = 2 * rayorig.dotProduct(raydir);
00300         Real c = rayorig.dotProduct(rayorig) - radius*radius;
00301 
00302         // Calc determinant
00303         Real d = (b*b) - (4 * a * c);
00304         if (d < 0)
00305         {
00306             // No intersection
00307             return std::pair<bool, Real>(false, 0);
00308         }
00309         else
00310         {
00311             // BTW, if d=0 there is one intersection, if d > 0 there are 2
00312             // But we only want the closest one, so that's ok, just use the 
00313             // '-' version of the solver
00314             Real t = ( -b - Math::Sqrt(d) ) / (2 * a);
00315             return std::pair<bool, Real>(true, t);
00316         }
00317 
00318 
00319     }
00320     //-----------------------------------------------------------------------
00321     std::pair<bool, Real> Math::intersects(const Ray& ray, const AxisAlignedBox& box)
00322     {
00323         if (box.isNull()) return std::pair<bool, Real>(false, 0);
00324 
00325         Real lowt = 0.0f;
00326         Real t;
00327         bool hit = false;
00328         Vector3 hitpoint;
00329         const Vector3& min = box.getMinimum();
00330         const Vector3& max = box.getMaximum();
00331         const Vector3& rayorig = ray.getOrigin();
00332         const Vector3& raydir = ray.getDirection();
00333 
00334         // Check origin inside first
00335         if ( rayorig > min && rayorig < max )
00336         {
00337             return std::pair<bool, Real>(true, 0);
00338         }
00339 
00340         // Check each face in turn, only check closest 3
00341         // Min x
00342         if (rayorig.x < min.x && raydir.x > 0)
00343         {
00344             t = (min.x - rayorig.x) / raydir.x;
00345             if (t > 0)
00346             {
00347                 // Substitute t back into ray and check bounds and dist
00348                 hitpoint = rayorig + raydir * t;
00349                 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
00350                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00351                     (!hit || t < lowt))
00352                 {
00353                     hit = true;
00354                     lowt = t;
00355                 }
00356             }
00357         }
00358         // Max x
00359         if (rayorig.x > max.x && raydir.x < 0)
00360         {
00361             t = (max.x - rayorig.x) / raydir.x;
00362             if (t > 0)
00363             {
00364                 // Substitute t back into ray and check bounds and dist
00365                 hitpoint = rayorig + raydir * t;
00366                 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
00367                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00368                     (!hit || t < lowt))
00369                 {
00370                     hit = true;
00371                     lowt = t;
00372                 }
00373             }
00374         }
00375         // Min y
00376         if (rayorig.y < min.y && raydir.y > 0)
00377         {
00378             t = (min.y - rayorig.y) / raydir.y;
00379             if (t > 0)
00380             {
00381                 // Substitute t back into ray and check bounds and dist
00382                 hitpoint = rayorig + raydir * t;
00383                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00384                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00385                     (!hit || t < lowt))
00386                 {
00387                     hit = true;
00388                     lowt = t;
00389                 }
00390             }
00391         }
00392         // Max y
00393         if (rayorig.y > max.y && raydir.y < 0)
00394         {
00395             t = (max.y - rayorig.y) / raydir.y;
00396             if (t > 0)
00397             {
00398                 // Substitute t back into ray and check bounds and dist
00399                 hitpoint = rayorig + raydir * t;
00400                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00401                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00402                     (!hit || t < lowt))
00403                 {
00404                     hit = true;
00405                     lowt = t;
00406                 }
00407             }
00408         }
00409         // Min z
00410         if (rayorig.z < min.z && raydir.z > 0)
00411         {
00412             t = (min.z - rayorig.z) / raydir.z;
00413             if (t > 0)
00414             {
00415                 // Substitute t back into ray and check bounds and dist
00416                 hitpoint = rayorig + raydir * t;
00417                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00418                     hitpoint.y >= min.y && hitpoint.y <= max.y &&
00419                     (!hit || t < lowt))
00420                 {
00421                     hit = true;
00422                     lowt = t;
00423                 }
00424             }
00425         }
00426         // Max z
00427         if (rayorig.z > max.z && raydir.z < 0)
00428         {
00429             t = (max.z - rayorig.z) / raydir.z;
00430             if (t > 0)
00431             {
00432                 // Substitute t back into ray and check bounds and dist
00433                 hitpoint = rayorig + raydir * t;
00434                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00435                     hitpoint.y >= min.y && hitpoint.y <= max.y &&
00436                     (!hit || t < lowt))
00437                 {
00438                     hit = true;
00439                     lowt = t;
00440                 }
00441             }
00442         }
00443 
00444         return std::pair<bool, Real>(hit, lowt);
00445 
00446     }
00447     //-----------------------------------------------------------------------
00448     bool Math::intersects(const Sphere& sphere, const AxisAlignedBox& box)
00449     {
00450         if (box.isNull()) return false;
00451 
00452         // Use splitting planes
00453         const Vector3& center = sphere.getCenter();
00454         Real radius = sphere.getRadius();
00455         const Vector3& min = box.getMinimum();
00456         const Vector3& max = box.getMaximum();
00457 
00458         // just test facing planes, early fail if sphere is totally outside
00459         if (center.x < min.x && 
00460             min.x - center.x > radius)
00461         {
00462             return false;
00463         }
00464         if (center.x > max.x && 
00465             center.x  - max.x > radius)
00466         {
00467             return false;
00468         }
00469 
00470         if (center.y < min.y && 
00471             min.y - center.y > radius)
00472         {
00473             return false;
00474         }
00475         if (center.y > max.y && 
00476             center.y  - max.y > radius)
00477         {
00478             return false;
00479         }
00480 
00481         if (center.z < min.z && 
00482             min.z - center.z > radius)
00483         {
00484             return false;
00485         }
00486         if (center.z > max.z && 
00487             center.z  - max.z > radius)
00488         {
00489             return false;
00490         }
00491 
00492         // Must intersect
00493         return true;
00494 
00495     }
00496     //-----------------------------------------------------------------------
00497     bool Math::intersects(const Plane& plane, const AxisAlignedBox& box)
00498     {
00499         if (box.isNull()) return false;
00500 
00501         // Get corners of the box
00502         const Vector3* pCorners = box.getAllCorners();
00503 
00504 
00505         // Test which side of the plane the corners are
00506         // Intersection occurs when at least one corner is on the 
00507         // opposite side to another
00508         Plane::Side lastSide = plane.getSide(pCorners[0]);
00509         for (int corner = 1; corner < 8; ++corner)
00510         {
00511             if (plane.getSide(pCorners[corner]) != lastSide)
00512             {
00513                 return true;
00514             }
00515         }
00516 
00517         return false;
00518     }
00519     //-----------------------------------------------------------------------
00520     bool Math::intersects(const Sphere& sphere, const Plane& plane)
00521     {
00522         return (
00523             Math::Abs(plane.normal.dotProduct(sphere.getCenter()))
00524             <= sphere.getRadius() );
00525     }
00526     //-----------------------------------------------------------------------
00527     Vector3 Math::calculateTangentSpaceVector(
00528         const Vector3& position1, const Vector3& position2, const Vector3& position3,
00529         Real u1, Real v1, Real u2, Real v2, Real u3, Real v3)
00530     {
00531         //side0 is the vector along one side of the triangle of vertices passed in, 
00532         //and side1 is the vector along another side. Taking the cross product of these returns the normal.
00533         Vector3 side0 = position1 - position2;
00534         Vector3 side1 = position3 - position1;
00535         //Calculate face normal
00536         Vector3 normal = side1.crossProduct(side0);
00537         normal.normalise();
00538         //Now we use a formula to calculate the tangent. 
00539         Real deltaV0 = v1 - v2;
00540         Real deltaV1 = v3 - v1;
00541         Vector3 tangent = deltaV1 * side0 - deltaV0 * side1;
00542         tangent.normalise();
00543         //Calculate binormal
00544         Real deltaU0 = u1 - u2;
00545         Real deltaU1 = u3 - u1;
00546         Vector3 binormal = deltaU1 * side0 - deltaU0 * side1;
00547         binormal.normalise();
00548         //Now, we take the cross product of the tangents to get a vector which 
00549         //should point in the same direction as our normal calculated above. 
00550         //If it points in the opposite direction (the dot product between the normals is less than zero), 
00551         //then we need to reverse the s and t tangents. 
00552         //This is because the triangle has been mirrored when going from tangent space to object space.
00553         //reverse tangents if necessary
00554         Vector3 tangentCross = tangent.crossProduct(binormal);
00555         if (tangentCross.dotProduct(normal) < 0.0f)
00556         {
00557             tangent = -tangent;
00558             binormal = -binormal;
00559         }
00560 
00561         return tangent;
00562 
00563     }
00564     //-----------------------------------------------------------------------
00565     Matrix4 Math::buildReflectionMatrix(const Plane& p)
00566     {
00567         return Matrix4(
00568             -2 * p.normal.x * p.normal.x + 1,   -2 * p.normal.x * p.normal.y,       -2 * p.normal.x * p.normal.z,       -2 * p.normal.x * p.d, 
00569             -2 * p.normal.y * p.normal.x,       -2 * p.normal.y * p.normal.y + 1,   -2 * p.normal.y * p.normal.z,       -2 * p.normal.y * p.d, 
00570             -2 * p.normal.z * p.normal.x,       -2 * p.normal.z * p.normal.y,       -2 * p.normal.z * p.normal.z + 1,   -2 * p.normal.z * p.d, 
00571             0,                                  0,                                  0,                                  1);
00572     }
00573 }

Copyright © 2002-2003 by The OGRE Team
Last modified Wed Jan 21 00:10:16 2004