| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- /*
- -----------------------------------------------------------------------------
- This source file is part of OGRE
- (Object-oriented Graphics Rendering Engine)
- For the latest info, see http://www.ogre3d.org/
- Copyright (c) 2000-2011 Torus Knot Software Ltd
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- -----------------------------------------------------------------------------
- */
- #include "OgreMath.h"
- #include "asm_math.h"
- #include "OgreVector2.h"
- #include "OgreVector3.h"
- #include "OgreVector4.h"
- #include "OgreRay.h"
- #include "OgreSphere.h"
- #include "OgreAxisAlignedBox.h"
- #include "OgrePlane.h"
- namespace CamelotEngine
- {
- const float Math::POS_INFINITY = std::numeric_limits<float>::infinity();
- const float Math::NEG_INFINITY = -std::numeric_limits<float>::infinity();
- const float Math::PI = float( 4.0 * atan( 1.0 ) );
- const float Math::TWO_PI = float( 2.0 * PI );
- const float Math::HALF_PI = float( 0.5 * PI );
- const float Math::fDeg2Rad = PI / float(180.0);
- const float Math::fRad2Deg = float(180.0) / PI;
- const float Math::LOG2 = log(float(2.0));
- int Math::mTrigTableSize;
- Math::AngleUnit Math::msAngleUnit;
- float Math::mTrigTableFactor;
- float *Math::mSinTable = NULL;
- float *Math::mTanTable = NULL;
- //-----------------------------------------------------------------------
- Math::Math( unsigned int trigTableSize )
- {
- msAngleUnit = AU_DEGREE;
- mTrigTableSize = trigTableSize;
- mTrigTableFactor = mTrigTableSize / Math::TWO_PI;
- mSinTable = (float*)malloc(sizeof(float) * mTrigTableSize);
- mTanTable = (float*)malloc(sizeof(float) * mTrigTableSize);
- buildTrigTables();
- }
- //-----------------------------------------------------------------------
- Math::~Math()
- {
- free(mSinTable);
- free(mTanTable);
- }
- //-----------------------------------------------------------------------
- void Math::buildTrigTables(void)
- {
- // Build trig lookup tables
- // Could get away with building only PI sized Sin table but simpler this
- // way. Who cares, it'll ony use an extra 8k of memory anyway and I like
- // simplicity.
- float angle;
- for (int i = 0; i < mTrigTableSize; ++i)
- {
- angle = Math::TWO_PI * i / mTrigTableSize;
- mSinTable[i] = sin(angle);
- mTanTable[i] = tan(angle);
- }
- }
- //-----------------------------------------------------------------------
- float Math::SinTable (float fValue)
- {
- // Convert range to index values, wrap if required
- int idx;
- if (fValue >= 0)
- {
- idx = int(fValue * mTrigTableFactor) % mTrigTableSize;
- }
- else
- {
- idx = mTrigTableSize - (int(-fValue * mTrigTableFactor) % mTrigTableSize) - 1;
- }
- return mSinTable[idx];
- }
- //-----------------------------------------------------------------------
- float Math::TanTable (float fValue)
- {
- // Convert range to index values, wrap if required
- int idx = int(fValue *= mTrigTableFactor) % mTrigTableSize;
- return mTanTable[idx];
- }
- //-----------------------------------------------------------------------
- int Math::ISign (int iValue)
- {
- return ( iValue > 0 ? +1 : ( iValue < 0 ? -1 : 0 ) );
- }
- //-----------------------------------------------------------------------
- Radian Math::ACos (float fValue)
- {
- if ( -1.0 < fValue )
- {
- if ( fValue < 1.0 )
- return Radian(acos(fValue));
- else
- return Radian(0.0);
- }
- else
- {
- return Radian(PI);
- }
- }
- //-----------------------------------------------------------------------
- Radian Math::ASin (float fValue)
- {
- if ( -1.0 < fValue )
- {
- if ( fValue < 1.0 )
- return Radian(asin(fValue));
- else
- return Radian(HALF_PI);
- }
- else
- {
- return Radian(-HALF_PI);
- }
- }
- //-----------------------------------------------------------------------
- float Math::Sign (float fValue)
- {
- if ( fValue > 0.0 )
- return 1.0;
- if ( fValue < 0.0 )
- return -1.0;
- return 0.0;
- }
- //-----------------------------------------------------------------------
- float Math::InvSqrt(float fValue)
- {
- return float(asm_rsq(fValue));
- }
- //-----------------------------------------------------------------------
- float Math::UnitRandom ()
- {
- return asm_rand() / asm_rand_max();
- }
-
- //-----------------------------------------------------------------------
- float Math::RangeRandom (float fLow, float fHigh)
- {
- return (fHigh-fLow)*UnitRandom() + fLow;
- }
- //-----------------------------------------------------------------------
- float Math::SymmetricRandom ()
- {
- return 2.0f * UnitRandom() - 1.0f;
- }
- //-----------------------------------------------------------------------
- void Math::setAngleUnit(Math::AngleUnit unit)
- {
- msAngleUnit = unit;
- }
- //-----------------------------------------------------------------------
- Math::AngleUnit Math::getAngleUnit(void)
- {
- return msAngleUnit;
- }
- //-----------------------------------------------------------------------
- float Math::AngleUnitsToRadians(float angleunits)
- {
- if (msAngleUnit == AU_DEGREE)
- return angleunits * fDeg2Rad;
- else
- return angleunits;
- }
- //-----------------------------------------------------------------------
- float Math::RadiansToAngleUnits(float radians)
- {
- if (msAngleUnit == AU_DEGREE)
- return radians * fRad2Deg;
- else
- return radians;
- }
- //-----------------------------------------------------------------------
- float Math::AngleUnitsToDegrees(float angleunits)
- {
- if (msAngleUnit == AU_RADIAN)
- return angleunits * fRad2Deg;
- else
- return angleunits;
- }
- //-----------------------------------------------------------------------
- float Math::DegreesToAngleUnits(float degrees)
- {
- if (msAngleUnit == AU_RADIAN)
- return degrees * fDeg2Rad;
- else
- return degrees;
- }
- //-----------------------------------------------------------------------
- bool Math::pointInTri2D(const Vector2& p, const Vector2& a,
- const Vector2& b, const Vector2& c)
- {
- // Winding must be consistent from all edges for point to be inside
- Vector2 v1, v2;
- float dot[3];
- bool zeroDot[3];
- v1 = b - a;
- v2 = p - a;
- // Note we don't care about normalisation here since sign is all we need
- // It means we don't have to worry about magnitude of cross products either
- dot[0] = v1.crossProduct(v2);
- zeroDot[0] = Math::RealEqual(dot[0], 0.0f, 1e-3f);
- v1 = c - b;
- v2 = p - b;
- dot[1] = v1.crossProduct(v2);
- zeroDot[1] = Math::RealEqual(dot[1], 0.0f, 1e-3f);
- // Compare signs (ignore colinear / coincident points)
- if(!zeroDot[0] && !zeroDot[1]
- && Math::Sign(dot[0]) != Math::Sign(dot[1]))
- {
- return false;
- }
- v1 = a - c;
- v2 = p - c;
- dot[2] = v1.crossProduct(v2);
- zeroDot[2] = Math::RealEqual(dot[2], 0.0f, 1e-3f);
- // Compare signs (ignore colinear / coincident points)
- if((!zeroDot[0] && !zeroDot[2]
- && Math::Sign(dot[0]) != Math::Sign(dot[2])) ||
- (!zeroDot[1] && !zeroDot[2]
- && Math::Sign(dot[1]) != Math::Sign(dot[2])))
- {
- return false;
- }
- return true;
- }
- //-----------------------------------------------------------------------
- bool Math::pointInTri3D(const Vector3& p, const Vector3& a,
- const Vector3& b, const Vector3& c, const Vector3& normal)
- {
- // Winding must be consistent from all edges for point to be inside
- Vector3 v1, v2;
- float dot[3];
- bool zeroDot[3];
- v1 = b - a;
- v2 = p - a;
- // Note we don't care about normalisation here since sign is all we need
- // It means we don't have to worry about magnitude of cross products either
- dot[0] = v1.crossProduct(v2).dotProduct(normal);
- zeroDot[0] = Math::RealEqual(dot[0], 0.0f, 1e-3f);
- v1 = c - b;
- v2 = p - b;
- dot[1] = v1.crossProduct(v2).dotProduct(normal);
- zeroDot[1] = Math::RealEqual(dot[1], 0.0f, 1e-3f);
- // Compare signs (ignore colinear / coincident points)
- if(!zeroDot[0] && !zeroDot[1]
- && Math::Sign(dot[0]) != Math::Sign(dot[1]))
- {
- return false;
- }
- v1 = a - c;
- v2 = p - c;
- dot[2] = v1.crossProduct(v2).dotProduct(normal);
- zeroDot[2] = Math::RealEqual(dot[2], 0.0f, 1e-3f);
- // Compare signs (ignore colinear / coincident points)
- if((!zeroDot[0] && !zeroDot[2]
- && Math::Sign(dot[0]) != Math::Sign(dot[2])) ||
- (!zeroDot[1] && !zeroDot[2]
- && Math::Sign(dot[1]) != Math::Sign(dot[2])))
- {
- return false;
- }
- return true;
- }
- //-----------------------------------------------------------------------
- bool Math::RealEqual( float a, float b, float tolerance )
- {
- if (fabs(b-a) <= tolerance)
- return true;
- else
- return false;
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray, const Plane& plane)
- {
- float denom = plane.normal.dotProduct(ray.getDirection());
- if (Math::Abs(denom) < std::numeric_limits<float>::epsilon())
- {
- // Parallel
- return std::pair<bool, float>(false, 0);
- }
- else
- {
- float nom = plane.normal.dotProduct(ray.getOrigin()) + plane.d;
- float t = -(nom/denom);
- return std::pair<bool, float>(t >= 0, t);
- }
-
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray,
- const vector<Plane>::type& planes, bool normalIsOutside)
- {
- list<Plane>::type planesList;
- for (vector<Plane>::type::const_iterator i = planes.begin(); i != planes.end(); ++i)
- {
- planesList.push_back(*i);
- }
- return intersects(ray, planesList, normalIsOutside);
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray,
- const list<Plane>::type& planes, bool normalIsOutside)
- {
- list<Plane>::type::const_iterator planeit, planeitend;
- planeitend = planes.end();
- bool allInside = true;
- std::pair<bool, float> ret;
- std::pair<bool, float> end;
- ret.first = false;
- ret.second = 0.0f;
- end.first = false;
- end.second = 0;
- // derive side
- // NB we don't pass directly since that would require Plane::Side in
- // interface, which results in recursive includes since Math is so fundamental
- Plane::Side outside = normalIsOutside ? Plane::POSITIVE_SIDE : Plane::NEGATIVE_SIDE;
- for (planeit = planes.begin(); planeit != planeitend; ++planeit)
- {
- const Plane& plane = *planeit;
- // is origin outside?
- if (plane.getSide(ray.getOrigin()) == outside)
- {
- allInside = false;
- // Test single plane
- std::pair<bool, float> planeRes =
- ray.intersects(plane);
- if (planeRes.first)
- {
- // Ok, we intersected
- ret.first = true;
- // Use the most distant result since convex volume
- ret.second = std::max(ret.second, planeRes.second);
- }
- else
- {
- ret.first =false;
- ret.second=0.0f;
- return ret;
- }
- }
- else
- {
- std::pair<bool, float> planeRes =
- ray.intersects(plane);
- if (planeRes.first)
- {
- if( !end.first )
- {
- end.first = true;
- end.second = planeRes.second;
- }
- else
- {
- end.second = std::min( planeRes.second, end.second );
- }
- }
- }
- }
- if (allInside)
- {
- // Intersecting at 0 distance since inside the volume!
- ret.first = true;
- ret.second = 0.0f;
- return ret;
- }
- if( end.first )
- {
- if( end.second < ret.second )
- {
- ret.first = false;
- return ret;
- }
- }
- return ret;
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray, const Sphere& sphere,
- bool discardInside)
- {
- const Vector3& raydir = ray.getDirection();
- // Adjust ray origin relative to sphere center
- const Vector3& rayorig = ray.getOrigin() - sphere.getCenter();
- float radius = sphere.getRadius();
- // Check origin inside first
- if (rayorig.squaredLength() <= radius*radius && discardInside)
- {
- return std::pair<bool, float>(true, 0);
- }
- // Mmm, quadratics
- // Build coeffs which can be used with std quadratic solver
- // ie t = (-b +/- sqrt(b*b + 4ac)) / 2a
- float a = raydir.dotProduct(raydir);
- float b = 2 * rayorig.dotProduct(raydir);
- float c = rayorig.dotProduct(rayorig) - radius*radius;
- // Calc determinant
- float d = (b*b) - (4 * a * c);
- if (d < 0)
- {
- // No intersection
- return std::pair<bool, float>(false, 0);
- }
- else
- {
- // BTW, if d=0 there is one intersection, if d > 0 there are 2
- // But we only want the closest one, so that's ok, just use the
- // '-' version of the solver
- float t = ( -b - Math::Sqrt(d) ) / (2 * a);
- if (t < 0)
- t = ( -b + Math::Sqrt(d) ) / (2 * a);
- return std::pair<bool, float>(true, t);
- }
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray, const AxisAlignedBox& box)
- {
- if (box.isNull()) return std::pair<bool, float>(false, 0);
- if (box.isInfinite()) return std::pair<bool, float>(true, 0);
- float lowt = 0.0f;
- float t;
- bool hit = false;
- Vector3 hitpoint;
- const Vector3& min = box.getMinimum();
- const Vector3& max = box.getMaximum();
- const Vector3& rayorig = ray.getOrigin();
- const Vector3& raydir = ray.getDirection();
- // Check origin inside first
- if ( rayorig > min && rayorig < max )
- {
- return std::pair<bool, float>(true, 0);
- }
- // Check each face in turn, only check closest 3
- // Min x
- if (rayorig.x <= min.x && raydir.x > 0)
- {
- t = (min.x - rayorig.x) / raydir.x;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
- hitpoint.z >= min.z && hitpoint.z <= max.z &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- // Max x
- if (rayorig.x >= max.x && raydir.x < 0)
- {
- t = (max.x - rayorig.x) / raydir.x;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
- hitpoint.z >= min.z && hitpoint.z <= max.z &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- // Min y
- if (rayorig.y <= min.y && raydir.y > 0)
- {
- t = (min.y - rayorig.y) / raydir.y;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
- hitpoint.z >= min.z && hitpoint.z <= max.z &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- // Max y
- if (rayorig.y >= max.y && raydir.y < 0)
- {
- t = (max.y - rayorig.y) / raydir.y;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
- hitpoint.z >= min.z && hitpoint.z <= max.z &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- // Min z
- if (rayorig.z <= min.z && raydir.z > 0)
- {
- t = (min.z - rayorig.z) / raydir.z;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
- hitpoint.y >= min.y && hitpoint.y <= max.y &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- // Max z
- if (rayorig.z >= max.z && raydir.z < 0)
- {
- t = (max.z - rayorig.z) / raydir.z;
- if (t >= 0)
- {
- // Substitute t back into ray and check bounds and dist
- hitpoint = rayorig + raydir * t;
- if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
- hitpoint.y >= min.y && hitpoint.y <= max.y &&
- (!hit || t < lowt))
- {
- hit = true;
- lowt = t;
- }
- }
- }
- return std::pair<bool, float>(hit, lowt);
- }
- //-----------------------------------------------------------------------
- bool Math::intersects(const Ray& ray, const AxisAlignedBox& box,
- float* d1, float* d2)
- {
- if (box.isNull())
- return false;
- if (box.isInfinite())
- {
- if (d1) *d1 = 0;
- if (d2) *d2 = Math::POS_INFINITY;
- return true;
- }
- const Vector3& min = box.getMinimum();
- const Vector3& max = box.getMaximum();
- const Vector3& rayorig = ray.getOrigin();
- const Vector3& raydir = ray.getDirection();
- Vector3 absDir;
- absDir[0] = Math::Abs(raydir[0]);
- absDir[1] = Math::Abs(raydir[1]);
- absDir[2] = Math::Abs(raydir[2]);
- // Sort the axis, ensure check minimise floating error axis first
- int imax = 0, imid = 1, imin = 2;
- if (absDir[0] < absDir[2])
- {
- imax = 2;
- imin = 0;
- }
- if (absDir[1] < absDir[imin])
- {
- imid = imin;
- imin = 1;
- }
- else if (absDir[1] > absDir[imax])
- {
- imid = imax;
- imax = 1;
- }
- float start = 0, end = Math::POS_INFINITY;
- #define _CALC_AXIS(i) \
- do { \
- float denom = 1 / raydir[i]; \
- float newstart = (min[i] - rayorig[i]) * denom; \
- float newend = (max[i] - rayorig[i]) * denom; \
- if (newstart > newend) std::swap(newstart, newend); \
- if (newstart > end || newend < start) return false; \
- if (newstart > start) start = newstart; \
- if (newend < end) end = newend; \
- } while(0)
- // Check each axis in turn
- _CALC_AXIS(imax);
- if (absDir[imid] < std::numeric_limits<float>::epsilon())
- {
- // Parallel with middle and minimise axis, check bounds only
- if (rayorig[imid] < min[imid] || rayorig[imid] > max[imid] ||
- rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
- return false;
- }
- else
- {
- _CALC_AXIS(imid);
- if (absDir[imin] < std::numeric_limits<float>::epsilon())
- {
- // Parallel with minimise axis, check bounds only
- if (rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
- return false;
- }
- else
- {
- _CALC_AXIS(imin);
- }
- }
- #undef _CALC_AXIS
- if (d1) *d1 = start;
- if (d2) *d2 = end;
- return true;
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray, const Vector3& a,
- const Vector3& b, const Vector3& c, const Vector3& normal,
- bool positiveSide, bool negativeSide)
- {
- //
- // Calculate intersection with plane.
- //
- float t;
- {
- float denom = normal.dotProduct(ray.getDirection());
- // Check intersect side
- if (denom > + std::numeric_limits<float>::epsilon())
- {
- if (!negativeSide)
- return std::pair<bool, float>(false, 0);
- }
- else if (denom < - std::numeric_limits<float>::epsilon())
- {
- if (!positiveSide)
- return std::pair<bool, float>(false, 0);
- }
- else
- {
- // Parallel or triangle area is close to zero when
- // the plane normal not normalised.
- return std::pair<bool, float>(false, 0);
- }
- t = normal.dotProduct(a - ray.getOrigin()) / denom;
- if (t < 0)
- {
- // Intersection is behind origin
- return std::pair<bool, float>(false, 0);
- }
- }
- //
- // Calculate the largest area projection plane in X, Y or Z.
- //
- size_t i0, i1;
- {
- float n0 = Math::Abs(normal[0]);
- float n1 = Math::Abs(normal[1]);
- float n2 = Math::Abs(normal[2]);
- i0 = 1; i1 = 2;
- if (n1 > n2)
- {
- if (n1 > n0) i0 = 0;
- }
- else
- {
- if (n2 > n0) i1 = 0;
- }
- }
- //
- // Check the intersection point is inside the triangle.
- //
- {
- float u1 = b[i0] - a[i0];
- float v1 = b[i1] - a[i1];
- float u2 = c[i0] - a[i0];
- float v2 = c[i1] - a[i1];
- float u0 = t * ray.getDirection()[i0] + ray.getOrigin()[i0] - a[i0];
- float v0 = t * ray.getDirection()[i1] + ray.getOrigin()[i1] - a[i1];
- float alpha = u0 * v2 - u2 * v0;
- float beta = u1 * v0 - u0 * v1;
- float area = u1 * v2 - u2 * v1;
- // epsilon to avoid float precision error
- const float EPSILON = 1e-6f;
- float tolerance = - EPSILON * area;
- if (area > 0)
- {
- if (alpha < tolerance || beta < tolerance || alpha+beta > area-tolerance)
- return std::pair<bool, float>(false, 0);
- }
- else
- {
- if (alpha > tolerance || beta > tolerance || alpha+beta < area-tolerance)
- return std::pair<bool, float>(false, 0);
- }
- }
- return std::pair<bool, float>(true, t);
- }
- //-----------------------------------------------------------------------
- std::pair<bool, float> Math::intersects(const Ray& ray, const Vector3& a,
- const Vector3& b, const Vector3& c,
- bool positiveSide, bool negativeSide)
- {
- Vector3 normal = calculateBasicFaceNormalWithoutNormalize(a, b, c);
- return intersects(ray, a, b, c, normal, positiveSide, negativeSide);
- }
- //-----------------------------------------------------------------------
- bool Math::intersects(const Sphere& sphere, const AxisAlignedBox& box)
- {
- if (box.isNull()) return false;
- if (box.isInfinite()) return true;
- // Use splitting planes
- const Vector3& center = sphere.getCenter();
- float radius = sphere.getRadius();
- const Vector3& min = box.getMinimum();
- const Vector3& max = box.getMaximum();
- // Arvo's algorithm
- float s, d = 0;
- for (int i = 0; i < 3; ++i)
- {
- if (center.ptr()[i] < min.ptr()[i])
- {
- s = center.ptr()[i] - min.ptr()[i];
- d += s * s;
- }
- else if(center.ptr()[i] > max.ptr()[i])
- {
- s = center.ptr()[i] - max.ptr()[i];
- d += s * s;
- }
- }
- return d <= radius * radius;
- }
- //-----------------------------------------------------------------------
- bool Math::intersects(const Plane& plane, const AxisAlignedBox& box)
- {
- return (plane.getSide(box) == Plane::BOTH_SIDE);
- }
- //-----------------------------------------------------------------------
- bool Math::intersects(const Sphere& sphere, const Plane& plane)
- {
- return (
- Math::Abs(plane.getDistance(sphere.getCenter()))
- <= sphere.getRadius() );
- }
- //-----------------------------------------------------------------------
- Vector3 Math::calculateTangentSpaceVector(
- const Vector3& position1, const Vector3& position2, const Vector3& position3,
- float u1, float v1, float u2, float v2, float u3, float v3)
- {
- //side0 is the vector along one side of the triangle of vertices passed in,
- //and side1 is the vector along another side. Taking the cross product of these returns the normal.
- Vector3 side0 = position1 - position2;
- Vector3 side1 = position3 - position1;
- //Calculate face normal
- Vector3 normal = side1.crossProduct(side0);
- normal.normalise();
- //Now we use a formula to calculate the tangent.
- float deltaV0 = v1 - v2;
- float deltaV1 = v3 - v1;
- Vector3 tangent = deltaV1 * side0 - deltaV0 * side1;
- tangent.normalise();
- //Calculate binormal
- float deltaU0 = u1 - u2;
- float deltaU1 = u3 - u1;
- Vector3 binormal = deltaU1 * side0 - deltaU0 * side1;
- binormal.normalise();
- //Now, we take the cross product of the tangents to get a vector which
- //should point in the same direction as our normal calculated above.
- //If it points in the opposite direction (the dot product between the normals is less than zero),
- //then we need to reverse the s and t tangents.
- //This is because the triangle has been mirrored when going from tangent space to object space.
- //reverse tangents if necessary
- Vector3 tangentCross = tangent.crossProduct(binormal);
- if (tangentCross.dotProduct(normal) < 0.0f)
- {
- tangent = -tangent;
- binormal = -binormal;
- }
- return tangent;
- }
- //-----------------------------------------------------------------------
- Matrix4 Math::buildReflectionMatrix(const Plane& p)
- {
- return Matrix4(
- -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,
- -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,
- -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,
- 0, 0, 0, 1);
- }
- //-----------------------------------------------------------------------
- Vector4 Math::calculateFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
- {
- Vector3 normal = calculateBasicFaceNormal(v1, v2, v3);
- // Now set up the w (distance of tri from origin
- return Vector4(normal.x, normal.y, normal.z, -(normal.dotProduct(v1)));
- }
- //-----------------------------------------------------------------------
- Vector3 Math::calculateBasicFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3)
- {
- Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
- normal.normalise();
- return normal;
- }
- //-----------------------------------------------------------------------
- Vector4 Math::calculateFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3)
- {
- Vector3 normal = calculateBasicFaceNormalWithoutNormalize(v1, v2, v3);
- // Now set up the w (distance of tri from origin)
- return Vector4(normal.x, normal.y, normal.z, -(normal.dotProduct(v1)));
- }
- //-----------------------------------------------------------------------
- Vector3 Math::calculateBasicFaceNormalWithoutNormalize(const Vector3& v1, const Vector3& v2, const Vector3& v3)
- {
- Vector3 normal = (v2 - v1).crossProduct(v3 - v1);
- return normal;
- }
- //-----------------------------------------------------------------------
- float Math::gaussianDistribution(float x, float offset, float scale)
- {
- float nom = Math::Exp(
- -Math::Sqr(x - offset) / (2 * Math::Sqr(scale)));
- float denom = scale * Math::Sqrt(2 * Math::PI);
- return nom / denom;
- }
- //---------------------------------------------------------------------
- Matrix4 Math::makeViewMatrix(const Vector3& position, const Quaternion& orientation,
- const Matrix4* reflectMatrix)
- {
- Matrix4 viewMatrix;
- // View matrix is:
- //
- // [ Lx Uy Dz Tx ]
- // [ Lx Uy Dz Ty ]
- // [ Lx Uy Dz Tz ]
- // [ 0 0 0 1 ]
- //
- // Where T = -(Transposed(Rot) * Pos)
- // This is most efficiently done using 3x3 Matrices
- Matrix3 rot;
- orientation.ToRotationMatrix(rot);
- // Make the translation relative to new axes
- Matrix3 rotT = rot.Transpose();
- Vector3 trans = -rotT * position;
- // Make final matrix
- viewMatrix = Matrix4::IDENTITY;
- viewMatrix = rotT; // fills upper 3x3
- viewMatrix[0][3] = trans.x;
- viewMatrix[1][3] = trans.y;
- viewMatrix[2][3] = trans.z;
- // Deal with reflections
- if (reflectMatrix)
- {
- viewMatrix = viewMatrix * (*reflectMatrix);
- }
- return viewMatrix;
- }
- //---------------------------------------------------------------------
- float Math::boundingRadiusFromAABB(const AxisAlignedBox& aabb)
- {
- Vector3 max = aabb.getMaximum();
- Vector3 min = aabb.getMinimum();
- Vector3 magnitude = max;
- magnitude.makeCeil(-max);
- magnitude.makeCeil(min);
- magnitude.makeCeil(-min);
- return magnitude.length();
- }
- }
|