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