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     Radian Math::ACos (Real fValue)
00124     {
00125         if ( -1.0 < fValue )
00126         {
00127             if ( fValue < 1.0 )
00128                 return Radian(acos(fValue));
00129             else
00130                 return Radian(0.0);
00131         }
00132         else
00133         {
00134             return Radian(PI);
00135         }
00136     }
00137     //-----------------------------------------------------------------------
00138     Radian Math::ASin (Real fValue)
00139     {
00140         if ( -1.0 < fValue )
00141         {
00142             if ( fValue < 1.0 )
00143                 return Radian(asin(fValue));
00144             else
00145                 return Radian(-HALF_PI);
00146         }
00147         else
00148         {
00149             return Radian(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     Real Math::AngleUnitsToDegrees(Real angleunits)
00216     {
00217        if (msAngleUnit == AU_RADIAN)
00218            return angleunits * fRad2Deg;
00219        else
00220            return angleunits;
00221     }
00222 
00223     //-----------------------------------------------------------------------
00224     Real Math::DegreesToAngleUnits(Real degrees)
00225     {
00226        if (msAngleUnit == AU_RADIAN)
00227            return degrees * fDeg2Rad;
00228        else
00229            return degrees;
00230     }
00231 
00232     //-----------------------------------------------------------------------
00233     bool Math::pointInTri2D( Real px, Real py, Real ax, Real ay, Real bx, Real by, Real cx, Real cy )
00234     {
00235         Real v1x, v2x, v1y, v2y;
00236         bool bClockwise;
00237 
00238         v1x = bx - ax;
00239         v1y = by - ay;
00240 
00241         v2x = px - bx;
00242         v2y = py - by;
00243 
00244         // For the sake of readability
00245         #define Clockwise ( v1x * v2y - v1y * v2x >= 0.0 )
00246 
00247         bClockwise = Clockwise;
00248 
00249         v1x = cx - bx;
00250         v1y = cy - by;
00251 
00252         v2x = px - cx;
00253         v2y = py - cy;
00254 
00255         if( Clockwise != bClockwise )
00256             return false;
00257 
00258         v1x = ax - cx;
00259         v1y = ay - cy;
00260 
00261         v2x = px - ax;
00262         v2y = py - ay;
00263 
00264         if( Clockwise != bClockwise )
00265             return false;
00266 
00267         return true;
00268 
00269         // Clean up the #defines
00270         #undef Clockwise
00271     }
00272 
00273     //-----------------------------------------------------------------------
00274     bool Math::RealEqual( Real a, Real b, Real tolerance )
00275     {
00276         if (fabs(b-a) <= tolerance)
00277             return true;
00278         else
00279             return false;
00280     }
00281 
00282     //-----------------------------------------------------------------------
00283     std::pair<bool, Real> Math::intersects(const Ray& ray, const Plane& plane)
00284     {
00285 
00286         Real denom = plane.normal.dotProduct(ray.getDirection());
00287         if (Math::Abs(denom) < std::numeric_limits<Real>::epsilon())
00288         {
00289             // Parallel
00290             return std::pair<bool, Real>(false, 0);
00291         }
00292         else
00293         {
00294             Real nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d;
00295             Real t = -(nom/denom);
00296             return std::pair<bool, Real>(t >= 0, t);
00297         }
00298         
00299     }
00300     //-----------------------------------------------------------------------
00301     std::pair<bool, Real> Math::intersects(const Ray& ray, const Sphere& sphere, 
00302         bool discardInside)
00303     {
00304         const Vector3& raydir = ray.getDirection();
00305         // Adjust ray origin relative to sphere center
00306         const Vector3& rayorig = ray.getOrigin() - sphere.getCenter();
00307         Real radius = sphere.getRadius();
00308 
00309         // Check origin inside first
00310         if (rayorig.squaredLength() <= radius*radius && discardInside)
00311         {
00312             return std::pair<bool, Real>(true, 0);
00313         }
00314 
00315         // Mmm, quadratics
00316         // Build coeffs which can be used with std quadratic solver
00317         // ie t = (-b +/- sqrt(b*b + 4ac)) / 2a
00318         Real a = raydir.dotProduct(raydir);
00319         Real b = 2 * rayorig.dotProduct(raydir);
00320         Real c = rayorig.dotProduct(rayorig) - radius*radius;
00321 
00322         // Calc determinant
00323         Real d = (b*b) - (4 * a * c);
00324         if (d < 0)
00325         {
00326             // No intersection
00327             return std::pair<bool, Real>(false, 0);
00328         }
00329         else
00330         {
00331             // BTW, if d=0 there is one intersection, if d > 0 there are 2
00332             // But we only want the closest one, so that's ok, just use the 
00333             // '-' version of the solver
00334             Real t = ( -b - Math::Sqrt(d) ) / (2 * a);
00335             if (t < 0)
00336                 t = ( -b + Math::Sqrt(d) ) / (2 * a);
00337             return std::pair<bool, Real>(true, t);
00338         }
00339 
00340 
00341     }
00342     //-----------------------------------------------------------------------
00343     std::pair<bool, Real> Math::intersects(const Ray& ray, const AxisAlignedBox& box)
00344     {
00345         if (box.isNull()) return std::pair<bool, Real>(false, 0);
00346 
00347         Real lowt = 0.0f;
00348         Real t;
00349         bool hit = false;
00350         Vector3 hitpoint;
00351         const Vector3& min = box.getMinimum();
00352         const Vector3& max = box.getMaximum();
00353         const Vector3& rayorig = ray.getOrigin();
00354         const Vector3& raydir = ray.getDirection();
00355 
00356         // Check origin inside first
00357         if ( rayorig > min && rayorig < max )
00358         {
00359             return std::pair<bool, Real>(true, 0);
00360         }
00361 
00362         // Check each face in turn, only check closest 3
00363         // Min x
00364         if (rayorig.x < min.x && raydir.x > 0)
00365         {
00366             t = (min.x - rayorig.x) / raydir.x;
00367             if (t > 0)
00368             {
00369                 // Substitute t back into ray and check bounds and dist
00370                 hitpoint = rayorig + raydir * t;
00371                 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
00372                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00373                     (!hit || t < lowt))
00374                 {
00375                     hit = true;
00376                     lowt = t;
00377                 }
00378             }
00379         }
00380         // Max x
00381         if (rayorig.x > max.x && raydir.x < 0)
00382         {
00383             t = (max.x - rayorig.x) / raydir.x;
00384             if (t > 0)
00385             {
00386                 // Substitute t back into ray and check bounds and dist
00387                 hitpoint = rayorig + raydir * t;
00388                 if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
00389                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00390                     (!hit || t < lowt))
00391                 {
00392                     hit = true;
00393                     lowt = t;
00394                 }
00395             }
00396         }
00397         // Min y
00398         if (rayorig.y < min.y && raydir.y > 0)
00399         {
00400             t = (min.y - rayorig.y) / raydir.y;
00401             if (t > 0)
00402             {
00403                 // Substitute t back into ray and check bounds and dist
00404                 hitpoint = rayorig + raydir * t;
00405                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00406                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00407                     (!hit || t < lowt))
00408                 {
00409                     hit = true;
00410                     lowt = t;
00411                 }
00412             }
00413         }
00414         // Max y
00415         if (rayorig.y > max.y && raydir.y < 0)
00416         {
00417             t = (max.y - rayorig.y) / raydir.y;
00418             if (t > 0)
00419             {
00420                 // Substitute t back into ray and check bounds and dist
00421                 hitpoint = rayorig + raydir * t;
00422                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00423                     hitpoint.z >= min.z && hitpoint.z <= max.z &&
00424                     (!hit || t < lowt))
00425                 {
00426                     hit = true;
00427                     lowt = t;
00428                 }
00429             }
00430         }
00431         // Min z
00432         if (rayorig.z < min.z && raydir.z > 0)
00433         {
00434             t = (min.z - rayorig.z) / raydir.z;
00435             if (t > 0)
00436             {
00437                 // Substitute t back into ray and check bounds and dist
00438                 hitpoint = rayorig + raydir * t;
00439                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00440                     hitpoint.y >= min.y && hitpoint.y <= max.y &&
00441                     (!hit || t < lowt))
00442                 {
00443                     hit = true;
00444                     lowt = t;
00445                 }
00446             }
00447         }
00448         // Max z
00449         if (rayorig.z > max.z && raydir.z < 0)
00450         {
00451             t = (max.z - rayorig.z) / raydir.z;
00452             if (t > 0)
00453             {
00454                 // Substitute t back into ray and check bounds and dist
00455                 hitpoint = rayorig + raydir * t;
00456                 if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
00457                     hitpoint.y >= min.y && hitpoint.y <= max.y &&
00458                     (!hit || t < lowt))
00459                 {
00460                     hit = true;
00461                     lowt = t;
00462                 }
00463             }
00464         }
00465 
00466         return std::pair<bool, Real>(hit, lowt);
00467 
00468     }
00469     //-----------------------------------------------------------------------
00470     bool Math::intersects(const Sphere& sphere, const AxisAlignedBox& box)
00471     {
00472         if (box.isNull()) return false;
00473 
00474         // Use splitting planes
00475         const Vector3& center = sphere.getCenter();
00476         Real radius = sphere.getRadius();
00477         const Vector3& min = box.getMinimum();
00478         const Vector3& max = box.getMaximum();
00479 
00480         // just test facing planes, early fail if sphere is totally outside
00481         if (center.x < min.x && 
00482             min.x - center.x > radius)
00483         {
00484             return false;
00485         }
00486         if (center.x > max.x && 
00487             center.x  - max.x > radius)
00488         {
00489             return false;
00490         }
00491 
00492         if (center.y < min.y && 
00493             min.y - center.y > radius)
00494         {
00495             return false;
00496         }
00497         if (center.y > max.y && 
00498             center.y  - max.y > radius)
00499         {
00500             return false;
00501         }
00502 
00503         if (center.z < min.z && 
00504             min.z - center.z > radius)
00505         {
00506             return false;
00507         }
00508         if (center.z > max.z && 
00509             center.z  - max.z > radius)
00510         {
00511             return false;
00512         }
00513 
00514         // Must intersect
00515         return true;
00516 
00517     }
00518     //-----------------------------------------------------------------------
00519     bool Math::intersects(const Plane& plane, const AxisAlignedBox& box)
00520     {
00521         if (box.isNull()) return false;
00522 
00523         // Get corners of the box
00524         const Vector3* pCorners = box.getAllCorners();
00525 
00526 
00527         // Test which side of the plane the corners are
00528         // Intersection occurs when at least one corner is on the 
00529         // opposite side to another
00530         Plane::Side lastSide = plane.getSide(pCorners[0]);
00531         for (int corner = 1; corner < 8; ++corner)
00532         {
00533             if (plane.getSide(pCorners[corner]) != lastSide)
00534             {
00535                 return true;
00536             }
00537         }
00538 
00539         return false;
00540     }
00541     //-----------------------------------------------------------------------
00542     bool Math::intersects(const Sphere& sphere, const Plane& plane)
00543     {
00544         return (
00545             Math::Abs(plane.normal.dotProduct(sphere.getCenter()))
00546             <= sphere.getRadius() );
00547     }
00548     //-----------------------------------------------------------------------
00549     Vector3 Math::calculateTangentSpaceVector(
00550         const Vector3& position1, const Vector3& position2, const Vector3& position3,
00551         Real u1, Real v1, Real u2, Real v2, Real u3, Real v3)
00552     {
00553         //side0 is the vector along one side of the triangle of vertices passed in, 
00554         //and side1 is the vector along another side. Taking the cross product of these returns the normal.
00555         Vector3 side0 = position1 - position2;
00556         Vector3 side1 = position3 - position1;
00557         //Calculate face normal
00558         Vector3 normal = side1.crossProduct(side0);
00559         normal.normalise();
00560         //Now we use a formula to calculate the tangent. 
00561         Real deltaV0 = v1 - v2;
00562         Real deltaV1 = v3 - v1;
00563         Vector3 tangent = deltaV1 * side0 - deltaV0 * side1;
00564         tangent.normalise();
00565         //Calculate binormal
00566         Real deltaU0 = u1 - u2;
00567         Real deltaU1 = u3 - u1;
00568         Vector3 binormal = deltaU1 * side0 - deltaU0 * side1;
00569         binormal.normalise();
00570         //Now, we take the cross product of the tangents to get a vector which 
00571         //should point in the same direction as our normal calculated above. 
00572         //If it points in the opposite direction (the dot product between the normals is less than zero), 
00573         //then we need to reverse the s and t tangents. 
00574         //This is because the triangle has been mirrored when going from tangent space to object space.
00575         //reverse tangents if necessary
00576         Vector3 tangentCross = tangent.crossProduct(binormal);
00577         if (tangentCross.dotProduct(normal) < 0.0f)
00578         {
00579             tangent = -tangent;
00580             binormal = -binormal;
00581         }
00582 
00583         return tangent;
00584 
00585     }
00586     //-----------------------------------------------------------------------
00587     Matrix4 Math::buildReflectionMatrix(const Plane& p)
00588     {
00589         return Matrix4(
00590             -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, 
00591             -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, 
00592             -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, 
00593             0,                                  0,                                  0,                                  1);
00594     }
00595     //-----------------------------------------------------------------------
00596     Vector4 Math::calculateFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
00597     {
00598         Vector3 normal = calculateBasicFaceNormal(v1, v2, v3);
00599         // Now set up the w (distance of tri from origin
00600         return Vector4(normal.x, normal.y, normal.z, -(normal.dotProduct(v1)));
00601     }
00602     //-----------------------------------------------------------------------
00603     Vector3 Math::calculateBasicFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
00604     {
00605         Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
00606         normal.normalise();
00607         return normal;
00608     }
00609 }

Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:32 2004