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