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