00001 /* 00002 ----------------------------------------------------------------------------- 00003 This source file is part of OGRE 00004 (Object-oriented Graphics Rendering Engine) 00005 For the latest info, see http://ogre.sourceforge.net/ 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 #include "OgreFrustum.h" 00027 00028 #include "OgreMath.h" 00029 #include "OgreMatrix3.h" 00030 #include "OgreSceneNode.h" 00031 #include "OgreSphere.h" 00032 #include "OgreLogManager.h" 00033 #include "OgreException.h" 00034 #include "OgreRoot.h" 00035 #include "OgreHardwareBufferManager.h" 00036 #include "OgreHardwareVertexBuffer.h" 00037 #include "OgreHardwareIndexBuffer.h" 00038 #include "OgreMaterialManager.h" 00039 00040 namespace Ogre { 00041 00042 String Frustum::msMovableType = "Frustum"; 00043 //----------------------------------------------------------------------- 00044 Frustum::Frustum() 00045 { 00046 // Reasonable defaults to Frustum params 00047 mFOVy = Math::RadiansToAngleUnits(Math::PI/4.0); 00048 mNearDist = 100.0f; 00049 mFarDist = 100000.0f; 00050 mAspect = 1.33333333333333f; 00051 00052 mRecalcFrustum = true; 00053 mRecalcView = true; 00054 00055 // Init matrices 00056 mViewMatrix = Matrix4::ZERO; 00057 mProjMatrix = Matrix4::ZERO; 00058 00059 // Initialise vertex & index data 00060 mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION); 00061 mVertexData.vertexCount = 24; 00062 mVertexData.vertexStart = 0; 00063 mVertexData.vertexBufferBinding->setBinding( 0, 00064 HardwareBufferManager::getSingleton().createVertexBuffer( 00065 sizeof(Real)*3, 24, HardwareBuffer::HBU_DYNAMIC) ); 00066 00067 // Initialise material 00068 mMaterial = static_cast<Material*>( 00069 MaterialManager::getSingleton().getByName("BaseWhiteNoLighting")); 00070 00071 // Default to not visible 00072 mVisible = false; 00073 00074 mParentNode = 0; 00075 00076 updateView(); 00077 } 00078 00079 //----------------------------------------------------------------------- 00080 Frustum::~Frustum() 00081 { 00082 // Do nothing 00083 } 00084 00085 //----------------------------------------------------------------------- 00086 void Frustum::setFOVy(Real fov) 00087 { 00088 mFOVy = fov; 00089 mRecalcFrustum = true; 00090 } 00091 00092 //----------------------------------------------------------------------- 00093 Real Frustum::getFOVy(void) const 00094 { 00095 return mFOVy; 00096 } 00097 00098 00099 //----------------------------------------------------------------------- 00100 void Frustum::setFarClipDistance(Real farPlane) 00101 { 00102 mFarDist = farPlane; 00103 mRecalcFrustum = true; 00104 } 00105 00106 //----------------------------------------------------------------------- 00107 Real Frustum::getFarClipDistance(void) const 00108 { 00109 return mFarDist; 00110 } 00111 00112 //----------------------------------------------------------------------- 00113 void Frustum::setNearClipDistance(Real nearPlane) 00114 { 00115 if (nearPlane <= 0) 00116 Except(Exception::ERR_INVALIDPARAMS, "Near clip distance must be greater than zero.", 00117 "Frustum::setNearClipDistance"); 00118 mNearDist = nearPlane; 00119 mRecalcFrustum = true; 00120 } 00121 00122 //----------------------------------------------------------------------- 00123 Real Frustum::getNearClipDistance(void) const 00124 { 00125 return mNearDist; 00126 } 00127 00128 //----------------------------------------------------------------------- 00129 const Matrix4& Frustum::getProjectionMatrix(void) const 00130 { 00131 00132 updateFrustum(); 00133 00134 return mProjMatrix; 00135 } 00136 //----------------------------------------------------------------------- 00137 const Matrix4& Frustum::getStandardProjectionMatrix(void) const 00138 { 00139 00140 updateFrustum(); 00141 00142 return mStandardProjMatrix; 00143 } 00144 //----------------------------------------------------------------------- 00145 const Matrix4& Frustum::getViewMatrix(void) const 00146 { 00147 updateView(); 00148 00149 return mViewMatrix; 00150 00151 } 00152 00153 //----------------------------------------------------------------------- 00154 const Plane& Frustum::getFrustumPlane(FrustumPlane plane) 00155 { 00156 // Make any pending updates to the calculated frustum 00157 updateView(); 00158 00159 return mFrustumPlanes[plane]; 00160 00161 } 00162 00163 //----------------------------------------------------------------------- 00164 bool Frustum::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) 00165 { 00166 // Null boxes always invisible 00167 if (bound.isNull()) return false; 00168 00169 // Make any pending updates to the calculated frustum 00170 updateView(); 00171 00172 // Get corners of the box 00173 const Vector3* pCorners = bound.getAllCorners(); 00174 00175 00176 // For each plane, see if all points are on the negative side 00177 // If so, object is not visible 00178 for (int plane = 0; plane < 6; ++plane) 00179 { 00180 if (mFrustumPlanes[plane].getSide(pCorners[0]) == Plane::NEGATIVE_SIDE && 00181 mFrustumPlanes[plane].getSide(pCorners[1]) == Plane::NEGATIVE_SIDE && 00182 mFrustumPlanes[plane].getSide(pCorners[2]) == Plane::NEGATIVE_SIDE && 00183 mFrustumPlanes[plane].getSide(pCorners[3]) == Plane::NEGATIVE_SIDE && 00184 mFrustumPlanes[plane].getSide(pCorners[4]) == Plane::NEGATIVE_SIDE && 00185 mFrustumPlanes[plane].getSide(pCorners[5]) == Plane::NEGATIVE_SIDE && 00186 mFrustumPlanes[plane].getSide(pCorners[6]) == Plane::NEGATIVE_SIDE && 00187 mFrustumPlanes[plane].getSide(pCorners[7]) == Plane::NEGATIVE_SIDE) 00188 { 00189 // ALL corners on negative side therefore out of view 00190 if (culledBy) 00191 *culledBy = (FrustumPlane)plane; 00192 return false; 00193 } 00194 00195 } 00196 00197 return true; 00198 } 00199 00200 //----------------------------------------------------------------------- 00201 bool Frustum::isVisible(const Vector3& vert, FrustumPlane* culledBy) 00202 { 00203 // Make any pending updates to the calculated frustum 00204 updateView(); 00205 00206 // For each plane, see if all points are on the negative side 00207 // If so, object is not visible 00208 for (int plane = 0; plane < 6; ++plane) 00209 { 00210 if (mFrustumPlanes[plane].getSide(vert) == Plane::NEGATIVE_SIDE) 00211 { 00212 // ALL corners on negative side therefore out of view 00213 if (culledBy) 00214 *culledBy = (FrustumPlane)plane; 00215 return false; 00216 } 00217 00218 } 00219 00220 return true; 00221 } 00222 00223 //----------------------------------------------------------------------- 00224 bool Frustum::isVisible(const Sphere& sphere, FrustumPlane* culledBy) 00225 { 00226 // Make any pending updates to the calculated frustum 00227 updateView(); 00228 00229 // For each plane, see if sphere is on negative side 00230 // If so, object is not visible 00231 for (int plane = 0; plane < 6; ++plane) 00232 { 00233 // If the distance from sphere center to plane is negative, and 'more negative' 00234 // than the radius of the sphere, sphere is outside frustum 00235 if (mFrustumPlanes[plane].getDistance(sphere.getCenter()) < -sphere.getRadius()) 00236 { 00237 // ALL corners on negative side therefore out of view 00238 if (culledBy) 00239 *culledBy = (FrustumPlane)plane; 00240 return false; 00241 } 00242 00243 } 00244 00245 return true; 00246 } 00247 //----------------------------------------------------------------------- 00248 void Frustum::updateFrustum(void) const 00249 { 00250 if (mRecalcFrustum) 00251 { 00252 // standard perspective transform, not API specific 00253 Real thetaY = Math::AngleUnitsToRadians(mFOVy * 0.5f); 00254 Real tanThetaY = Math::Tan(thetaY); 00255 00256 // Calc matrix elements 00257 Real w = (1.0f / tanThetaY) / mAspect; 00258 Real h = 1.0f / tanThetaY; 00259 Real q = -(mFarDist + mNearDist) / (mFarDist - mNearDist); 00260 //Real qn= q * mNearDist; 00261 Real qn = -2 * (mFarDist * mNearDist) / (mFarDist - mNearDist); 00262 00263 // NB This creates Z in range [-1,1] 00264 // 00265 // [ w 0 0 0 ] 00266 // [ 0 h 0 0 ] 00267 // [ 0 0 q qn ] 00268 // [ 0 0 -1 0 ] 00269 00270 mProjMatrix = Matrix4::ZERO; 00271 mProjMatrix[0][0] = w; 00272 mProjMatrix[1][1] = h; 00273 mProjMatrix[2][2] = q; 00274 mProjMatrix[2][3] = qn; 00275 mProjMatrix[3][2] = -1; 00276 00277 // Calculate co-efficients for the frustum planes 00278 // Special-cased for L = -R and B = -T i.e. viewport centered 00279 // on direction vector. 00280 // Taken from ideas in WildMagic 0.2 http://www.magic-software.com 00281 //Real thetaX = thetaY * mAspect; 00282 Real tanThetaX = tanThetaY * mAspect; 00283 00284 Real vpTop = tanThetaY * mNearDist; 00285 Real vpRight = tanThetaX * mNearDist; 00286 Real vpBottom = -vpTop; 00287 Real vpLeft = -vpRight; 00288 00289 Real fNSqr = mNearDist * mNearDist; 00290 Real fLSqr = vpRight * vpRight; 00291 Real fRSqr = fLSqr; 00292 Real fTSqr = vpTop * vpTop; 00293 Real fBSqr = fTSqr; 00294 00295 Real fInvLength = 1.0 / Math::Sqrt( fNSqr + fLSqr ); 00296 mCoeffL[0] = mNearDist * fInvLength; 00297 mCoeffL[1] = -vpLeft * fInvLength; 00298 00299 fInvLength = 1.0 / Math::Sqrt( fNSqr + fRSqr ); 00300 mCoeffR[0] = -mNearDist * fInvLength; 00301 mCoeffR[1] = vpRight * fInvLength; 00302 00303 fInvLength = 1.0 / Math::Sqrt( fNSqr + fBSqr ); 00304 mCoeffB[0] = mNearDist * fInvLength; 00305 mCoeffB[1] = -vpBottom * fInvLength; 00306 00307 fInvLength = 1.0 / Math::Sqrt( fNSqr + fTSqr ); 00308 mCoeffT[0] = -mNearDist * fInvLength; 00309 mCoeffT[1] = vpTop * fInvLength; 00310 00311 00312 // Calculate bounding box 00313 // Box is from 0, down -Z, max dimensions as determined from far plane 00314 Real farTop = tanThetaY * mFarDist; 00315 Real farRight = tanThetaX * mFarDist; 00316 Real farBottom = -farTop; 00317 Real farLeft = -farRight; 00318 Vector3 min(-farRight, -farTop, 0); 00319 Vector3 max(farRight, farTop, mFarDist); 00320 mBoundingBox.setExtents(min, max); 00321 00322 // Calculate vertex positions 00323 // 0 is the origin 00324 // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise 00325 // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise 00326 HardwareVertexBufferSharedPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0); 00327 Real* pReal = static_cast<Real*>(vbuf->lock(HardwareBuffer::HBL_DISCARD)); 00328 00329 // near plane (remember frustum is going in -Z direction) 00330 *pReal++ = vpLeft; *pReal++ = vpTop; *pReal++ = -mNearDist; 00331 *pReal++ = vpRight; *pReal++ = vpTop; *pReal++ = -mNearDist; 00332 00333 *pReal++ = vpRight; *pReal++ = vpTop; *pReal++ = -mNearDist; 00334 *pReal++ = vpRight; *pReal++ = vpBottom; *pReal++ = -mNearDist; 00335 00336 *pReal++ = vpRight; *pReal++ = vpBottom; *pReal++ = -mNearDist; 00337 *pReal++ = vpLeft; *pReal++ = vpBottom; *pReal++ = -mNearDist; 00338 00339 *pReal++ = vpLeft; *pReal++ = vpBottom; *pReal++ = -mNearDist; 00340 *pReal++ = vpLeft; *pReal++ = vpTop; *pReal++ = -mNearDist; 00341 00342 // far plane (remember frustum is going in -Z direction) 00343 *pReal++ = farLeft; *pReal++ = farTop; *pReal++ = -mFarDist; 00344 *pReal++ = farRight; *pReal++ = farTop; *pReal++ = -mFarDist; 00345 00346 *pReal++ = farRight; *pReal++ = farTop; *pReal++ = -mFarDist; 00347 *pReal++ = farRight; *pReal++ = farBottom; *pReal++ = -mFarDist; 00348 00349 *pReal++ = farRight; *pReal++ = farBottom; *pReal++ = -mFarDist; 00350 *pReal++ = farLeft; *pReal++ = farBottom; *pReal++ = -mFarDist; 00351 00352 *pReal++ = farLeft; *pReal++ = farBottom; *pReal++ = -mFarDist; 00353 *pReal++ = farLeft; *pReal++ = farTop; *pReal++ = -mFarDist; 00354 00355 // Sides of the pyramid 00356 *pReal++ = 0.0f; *pReal++ = 0.0f; *pReal++ = 0.0f; 00357 *pReal++ = farLeft; *pReal++ = farTop; *pReal++ = -mFarDist; 00358 00359 *pReal++ = 0.0f; *pReal++ = 0.0f; *pReal++ = 0.0f; 00360 *pReal++ = farRight; *pReal++ = farTop; *pReal++ = -mFarDist; 00361 00362 *pReal++ = 0.0f; *pReal++ = 0.0f; *pReal++ = 0.0f; 00363 *pReal++ = farRight; *pReal++ = farBottom; *pReal++ = -mFarDist; 00364 00365 *pReal++ = 0.0f; *pReal++ = 0.0f; *pReal++ = 0.0f; 00366 *pReal++ = farLeft; *pReal++ = farBottom; *pReal++ = -mFarDist; 00367 00368 00369 00370 vbuf->unlock(); 00371 00372 mRecalcFrustum = false; 00373 } 00374 } 00375 00376 //----------------------------------------------------------------------- 00377 bool Frustum::isViewOutOfDate(void) const 00378 { 00379 // Attached to node? 00380 if (mParentNode) 00381 { 00382 if (!mRecalcView && mParentNode->_getDerivedOrientation() == mLastParentOrientation && 00383 mParentNode->_getDerivedPosition() == mLastParentPosition) 00384 { 00385 return false; 00386 } 00387 else 00388 { 00389 // Ok, we're out of date with SceneNode we're attached to 00390 mLastParentOrientation = mParentNode->_getDerivedOrientation(); 00391 mLastParentPosition = mParentNode->_getDerivedPosition(); 00392 return true; 00393 } 00394 } 00395 return mRecalcView; 00396 } 00397 00398 //----------------------------------------------------------------------- 00399 bool Frustum::isFrustumOutOfDate(void) const 00400 { 00401 return mRecalcFrustum; 00402 } 00403 00404 //----------------------------------------------------------------------- 00405 void Frustum::updateView(void) const 00406 { 00407 if (isViewOutOfDate()) 00408 { 00409 // ---------------------- 00410 // Update the view matrix 00411 // ---------------------- 00412 00413 // View matrix is: 00414 // 00415 // [ Lx Uy Dz Tx ] 00416 // [ Lx Uy Dz Ty ] 00417 // [ Lx Uy Dz Tz ] 00418 // [ 0 0 0 1 ] 00419 // 00420 // Where T = -(Transposed(Rot) * Pos) 00421 00422 // This is most efficiently done using 3x3 Matrices 00423 00424 // Get orientation from quaternion 00425 00426 Matrix3 rot; 00427 mLastParentOrientation.ToRotationMatrix(rot); 00428 Vector3 left = rot.GetColumn(0); 00429 Vector3 up = rot.GetColumn(1); 00430 Vector3 direction = rot.GetColumn(2); 00431 00432 00433 // Make the translation relative to new axes 00434 Matrix3 rotT = rot.Transpose(); 00435 Vector3 trans = -rotT * mLastParentPosition; 00436 00437 // Make final matrix 00438 // Matrix is pre-zeroised in constructor 00439 mViewMatrix = rotT; // fills upper 3x3 00440 mViewMatrix[0][3] = trans.x; 00441 mViewMatrix[1][3] = trans.y; 00442 mViewMatrix[2][3] = trans.z; 00443 mViewMatrix[3][3] = 1.0f; 00444 00445 // ------------------------- 00446 // Update the frustum planes 00447 // ------------------------- 00448 updateFrustum(); 00449 // Use Frustum view direction for frustum, which is -Z not Z as for matrix calc 00450 Vector3 camDirection = mLastParentOrientation* -Vector3::UNIT_Z; 00451 // Calc distance along direction to position 00452 Real fDdE = camDirection.dotProduct(mLastParentPosition); 00453 00454 // left plane 00455 mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal = mCoeffL[0]*left + 00456 mCoeffL[1]*camDirection; 00457 mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = 00458 -mLastParentPosition.dotProduct(mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal); 00459 00460 // right plane 00461 mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal = mCoeffR[0]*left + 00462 mCoeffR[1]*camDirection; 00463 mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = 00464 -mLastParentPosition.dotProduct(mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal); 00465 00466 // bottom plane 00467 mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal = mCoeffB[0]*up + 00468 mCoeffB[1]*camDirection; 00469 mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = 00470 -mLastParentPosition.dotProduct(mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal); 00471 00472 // top plane 00473 mFrustumPlanes[FRUSTUM_PLANE_TOP].normal = mCoeffT[0]*up + 00474 mCoeffT[1]*camDirection; 00475 mFrustumPlanes[FRUSTUM_PLANE_TOP].d = 00476 -mLastParentPosition.dotProduct(mFrustumPlanes[FRUSTUM_PLANE_TOP].normal); 00477 00478 // far plane 00479 mFrustumPlanes[FRUSTUM_PLANE_FAR].normal = -camDirection; 00480 // d is distance along normal to origin 00481 mFrustumPlanes[FRUSTUM_PLANE_FAR].d = fDdE + mFarDist; 00482 00483 // near plane 00484 mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal = camDirection; 00485 mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = -(fDdE + mNearDist); 00486 00487 00488 00489 00490 mRecalcView = false; 00491 00492 } 00493 00494 } 00495 00496 //----------------------------------------------------------------------- 00497 Real Frustum::getAspectRatio(void) const 00498 { 00499 return mAspect; 00500 } 00501 00502 //----------------------------------------------------------------------- 00503 void Frustum::setAspectRatio(Real r) 00504 { 00505 mAspect = r; 00506 mRecalcFrustum = true; 00507 } 00508 00509 //----------------------------------------------------------------------- 00510 const AxisAlignedBox& Frustum::getBoundingBox(void) const 00511 { 00512 return mBoundingBox; 00513 } 00514 //----------------------------------------------------------------------- 00515 void Frustum::_updateRenderQueue(RenderQueue* queue) 00516 { 00517 // Add self 00518 queue->addRenderable(this); 00519 } 00520 //----------------------------------------------------------------------- 00521 const String& Frustum::getMovableType(void) const 00522 { 00523 return msMovableType; 00524 } 00525 //----------------------------------------------------------------------- 00526 Real Frustum::getBoundingRadius(void) const 00527 { 00528 return mFarDist; 00529 } 00530 //----------------------------------------------------------------------- 00531 Material* Frustum::getMaterial(void) const 00532 { 00533 return mMaterial; 00534 } 00535 //----------------------------------------------------------------------- 00536 void Frustum::getRenderOperation(RenderOperation& op) 00537 { 00538 updateView(); 00539 updateFrustum(); 00540 op.operationType = RenderOperation::OT_LINE_LIST; 00541 op.useIndexes = false; 00542 op.vertexData = &mVertexData; 00543 } 00544 //----------------------------------------------------------------------- 00545 void Frustum::getWorldTransforms(Matrix4* xform) const 00546 { 00547 if (mParentNode) 00548 mParentNode->getWorldTransforms(xform); 00549 } 00550 //----------------------------------------------------------------------- 00551 const Quaternion& Frustum::getWorldOrientation(void) const 00552 { 00553 if (mParentNode) 00554 return mParentNode->_getDerivedOrientation(); 00555 else 00556 return Quaternion::IDENTITY; 00557 } 00558 //----------------------------------------------------------------------- 00559 const Vector3& Frustum::getWorldPosition(void) const 00560 { 00561 if (mParentNode) 00562 return mParentNode->_getDerivedPosition(); 00563 else 00564 return Vector3::ZERO; 00565 } 00566 //----------------------------------------------------------------------- 00567 Real Frustum::getSquaredViewDepth(const Camera* cam) const 00568 { 00569 // Calc from centre 00570 if (mParentNode) 00571 return (cam->getDerivedPosition() 00572 - mParentNode->_getDerivedPosition()).squaredLength(); 00573 else 00574 return 0; 00575 } 00576 //----------------------------------------------------------------------- 00577 const LightList& Frustum::getLights(void) const 00578 { 00579 // N/A 00580 static LightList ll; 00581 return ll; 00582 } 00583 //----------------------------------------------------------------------- 00584 const String& Frustum::getName(void) const 00585 { 00586 // NA 00587 return msMovableType; 00588 } 00589 //----------------------------------------------------------------------- 00590 void Frustum::_notifyCurrentCamera(Camera* cam) 00591 { 00592 // NA 00593 } 00594 00595 00596 } // namespace Ogre
Copyright © 2002-2003 by The OGRE Team
Last modified Wed Jan 21 00:10:10 2004