Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

OgreFrustum.cpp

Go to the documentation of this file.
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