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

Copyright © 2002-2003 by The OGRE Team
Last modified Fri May 14 23:22:22 2004