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