source: OGRE/trunk/ogre_changes/Ogre1.2/OgreMain/src/OgreFrustum.cpp @ 921

Revision 921, 40.7 KB checked in by mattausch, 18 years ago (diff)

added updates for visibility

Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://ogre.sourceforge.net/
6
7Copyright (c) 2000-2005 The OGRE Team
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23-----------------------------------------------------------------------------
24*/
25#include "OgreStableHeaders.h"
26#include "OgreFrustum.h"
27
28#include "OgreMath.h"
29#include "OgreMatrix3.h"
30#include "OgreSceneNode.h"
31#include "OgreSphere.h"
32#include "OgreLogManager.h"
33#include "OgreException.h"
34#include "OgreRoot.h"
35#include "OgreCamera.h"
36#include "OgreHardwareBufferManager.h"
37#include "OgreHardwareVertexBuffer.h"
38#include "OgreHardwareIndexBuffer.h"
39#include "OgreMaterialManager.h"
40#include "OgreRenderSystem.h"
41
42namespace Ogre {
43
44    String Frustum::msMovableType = "Frustum";
45    const Real Frustum::INFINITE_FAR_PLANE_ADJUST = 0.00001;
46    //-----------------------------------------------------------------------
47    Frustum::Frustum() :
48        mProjType(PT_PERSPECTIVE),
49        mFOVy(Radian(Math::PI/4.0)),
50        mFarDist(100000.0f),
51        mNearDist(100.0f),
52        mAspect(1.33333333333333f),
53        mFrustumOffset(Vector2::ZERO),
54        mFocalLength(1.0f),
55        mLastParentOrientation(Quaternion::IDENTITY),
56        mLastParentPosition(Vector3::ZERO),
57        mRecalcFrustum(true),
58        mRecalcView(true),
59        mRecalcFrustumPlanes(true),
60        mRecalcWorldSpaceCorners(true),
61        mRecalcVertexData(true),
62                mCustomViewMatrix(false),
63                mCustomProjMatrix(false),
64        mReflect(false),
65        mLinkedReflectPlane(0),
66        mObliqueDepthProjection(false),
67        mLinkedObliqueProjPlane(0)
68    {
69        // Initialise material
70        mMaterial = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
71       
72        // Alter superclass members
73        mVisible = false;
74        mParentNode = 0;
75
76        mLastLinkedReflectionPlane.normal = Vector3::ZERO;
77        mLastLinkedObliqueProjPlane.normal = Vector3::ZERO;
78
79
80        updateView();
81        updateFrustum();
82    }
83
84    //-----------------------------------------------------------------------
85    Frustum::~Frustum()
86    {
87        // Do nothing
88    }
89
90    //-----------------------------------------------------------------------
91    void Frustum::setFOVy(const Radian& fov)
92    {
93        mFOVy = fov;
94        invalidateFrustum();
95    }
96
97    //-----------------------------------------------------------------------
98    const Radian& Frustum::getFOVy(void) const
99    {
100        return mFOVy;
101    }
102
103
104    //-----------------------------------------------------------------------
105    void Frustum::setFarClipDistance(Real farPlane)
106    {
107        mFarDist = farPlane;
108        invalidateFrustum();
109    }
110
111    //-----------------------------------------------------------------------
112    Real Frustum::getFarClipDistance(void) const
113    {
114        return mFarDist;
115    }
116
117    //-----------------------------------------------------------------------
118    void Frustum::setNearClipDistance(Real nearPlane)
119    {
120        if (nearPlane <= 0)
121            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Near clip distance must be greater than zero.",
122                "Frustum::setNearClipDistance");
123        mNearDist = nearPlane;
124        invalidateFrustum();
125    }
126
127    //-----------------------------------------------------------------------
128    Real Frustum::getNearClipDistance(void) const
129    {
130        return mNearDist;
131    }
132
133    //---------------------------------------------------------------------
134    void Frustum::setFrustumOffset(const Vector2& offset)
135    {
136        mFrustumOffset = offset;
137        invalidateFrustum();
138    }
139    //---------------------------------------------------------------------
140    void Frustum::setFrustumOffset(Real horizontal, Real vertical)
141    {
142        setFrustumOffset(Vector2(horizontal, vertical));
143    }
144    //---------------------------------------------------------------------
145    const Vector2& Frustum::getFrustumOffset() const
146    {
147        return mFrustumOffset;
148    }
149    //---------------------------------------------------------------------
150    void Frustum::setFocalLength(Real focalLength)
151    {
152        if (focalLength <= 0)
153        {
154            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
155                "Focal length must be greater than zero.",
156                "Frustum::setFocalLength");
157        }
158
159        mFocalLength = focalLength;
160        invalidateFrustum();
161    }
162    //---------------------------------------------------------------------
163    Real Frustum::getFocalLength() const
164    {
165        return mFocalLength;
166    }
167    //-----------------------------------------------------------------------
168    const Matrix4& Frustum::getProjectionMatrix(void) const
169    {
170
171        updateFrustum();
172
173        return mProjMatrix;
174    }
175    //-----------------------------------------------------------------------
176    const Matrix4& Frustum::getProjectionMatrixWithRSDepth(void) const
177    {
178
179        updateFrustum();
180
181        return mProjMatrixRSDepth;
182    }
183    //-----------------------------------------------------------------------
184    const Matrix4& Frustum::getProjectionMatrixRS(void) const
185    {
186
187        updateFrustum();
188
189        return mProjMatrixRS;
190    }
191    //-----------------------------------------------------------------------
192    const Matrix4& Frustum::getViewMatrix(void) const
193    {
194        updateView();
195
196        return mViewMatrix;
197
198    }
199
200    //-----------------------------------------------------------------------
201    const Plane* Frustum::getFrustumPlanes(void) const
202    {
203        // Make any pending updates to the calculated frustum planes
204        updateFrustumPlanes();
205
206        return mFrustumPlanes;
207    }
208
209    //-----------------------------------------------------------------------
210    const Plane& Frustum::getFrustumPlane(unsigned short plane) const
211    {
212        // Make any pending updates to the calculated frustum planes
213        updateFrustumPlanes();
214
215        return mFrustumPlanes[plane];
216
217    }
218
219    //-----------------------------------------------------------------------
220    bool Frustum::isVisible(const AxisAlignedBox& bound, FrustumPlane* culledBy) const
221    {
222        // Null boxes always invisible
223        if (bound.isNull()) return false;
224
225        // Make any pending updates to the calculated frustum planes
226        updateFrustumPlanes();
227
228        // Get corners of the box
229        const Vector3* pCorners = bound.getAllCorners();
230
231
232        // For each plane, see if all points are on the negative side
233        // If so, object is not visible
234        for (int plane = 0; plane < 6; ++plane)
235        {
236            // Skip far plane if infinite view frustum
237            if (mFarDist == 0 && plane == FRUSTUM_PLANE_FAR)
238                continue;
239
240            if (mFrustumPlanes[plane].getSide(pCorners[0]) == Plane::NEGATIVE_SIDE &&
241                mFrustumPlanes[plane].getSide(pCorners[1]) == Plane::NEGATIVE_SIDE &&
242                mFrustumPlanes[plane].getSide(pCorners[2]) == Plane::NEGATIVE_SIDE &&
243                mFrustumPlanes[plane].getSide(pCorners[3]) == Plane::NEGATIVE_SIDE &&
244                mFrustumPlanes[plane].getSide(pCorners[4]) == Plane::NEGATIVE_SIDE &&
245                mFrustumPlanes[plane].getSide(pCorners[5]) == Plane::NEGATIVE_SIDE &&
246                mFrustumPlanes[plane].getSide(pCorners[6]) == Plane::NEGATIVE_SIDE &&
247                mFrustumPlanes[plane].getSide(pCorners[7]) == Plane::NEGATIVE_SIDE)
248            {
249                // ALL corners on negative side therefore out of view
250                if (culledBy)
251                    *culledBy = (FrustumPlane)plane;
252                return false;
253            }
254
255        }
256
257        return true;
258    }
259
260    //-----------------------------------------------------------------------
261    bool Frustum::isVisible(const Vector3& vert, FrustumPlane* culledBy) const
262    {
263        // Make any pending updates to the calculated frustum planes
264        updateFrustumPlanes();
265
266        // For each plane, see if all points are on the negative side
267        // If so, object is not visible
268        for (int plane = 0; plane < 6; ++plane)
269        {
270            // Skip far plane if infinite view frustum
271            if (mFarDist == 0 && plane == FRUSTUM_PLANE_FAR)
272                continue;
273
274            if (mFrustumPlanes[plane].getSide(vert) == Plane::NEGATIVE_SIDE)
275            {
276                // ALL corners on negative side therefore out of view
277                if (culledBy)
278                    *culledBy = (FrustumPlane)plane;
279                return false;
280            }
281
282        }
283
284        return true;
285    }
286
287    //-----------------------------------------------------------------------
288    bool Frustum::isVisible(const Sphere& sphere, FrustumPlane* culledBy) const
289    {
290        // Make any pending updates to the calculated frustum planes
291        updateFrustumPlanes();
292
293        // For each plane, see if sphere is on negative side
294        // If so, object is not visible
295        for (int plane = 0; plane < 6; ++plane)
296        {
297            // Skip far plane if infinite view frustum
298            if (mFarDist == 0 && plane == FRUSTUM_PLANE_FAR)
299                continue;
300
301            // If the distance from sphere center to plane is negative, and 'more negative'
302            // than the radius of the sphere, sphere is outside frustum
303            if (mFrustumPlanes[plane].getDistance(sphere.getCenter()) < -sphere.getRadius())
304            {
305                // ALL corners on negative side therefore out of view
306                if (culledBy)
307                    *culledBy = (FrustumPlane)plane;
308                return false;
309            }
310
311        }
312
313        return true;
314    }
315    //-----------------------------------------------------------------------
316    void Frustum::calcProjectionParameters(Real& left, Real& right, Real& bottom, Real& top) const
317    {
318                if (mCustomProjMatrix)
319                {
320                        // Convert clipspace corners to camera space
321                        Matrix4 invProj = mProjMatrix.inverse();
322                        Vector3 topLeft(-0.5f, 0.5f, 0.0f);
323                        Vector3 bottomRight(0.5f, -0.5f, 0.0f);
324
325                        topLeft = invProj * topLeft;
326                        bottomRight = invProj * bottomRight;
327
328                        left = topLeft.x;
329                        top = topLeft.y;
330                        right = bottomRight.x;
331                        bottom = bottomRight.y;
332
333                }
334                else
335                {
336                        // Calculate general projection parameters
337
338                        Radian thetaY (mFOVy * 0.5f);
339                        Real tanThetaY = Math::Tan(thetaY);
340                        Real tanThetaX = tanThetaY * mAspect;
341
342                        // Unknow how to apply frustum offset to orthographic camera, just ignore here
343                        Real nearFocal = (mProjType == PT_PERSPECTIVE) ? mNearDist / mFocalLength : 0;
344                        Real nearOffsetX = mFrustumOffset.x * nearFocal;
345                        Real nearOffsetY = mFrustumOffset.y * nearFocal;
346                        Real half_w = tanThetaX * mNearDist;
347                        Real half_h = tanThetaY * mNearDist;
348
349                        left   = - half_w + nearOffsetX;
350                        right  = + half_w + nearOffsetX;
351                        bottom = - half_h + nearOffsetY;
352                        top    = + half_h + nearOffsetY;
353                }
354    }
355        //-----------------------------------------------------------------------
356        void Frustum::updateFrustumImpl(void) const
357        {
358                // Common calcs
359                Real left, right, bottom, top;
360                calcProjectionParameters(left, right, bottom, top);
361
362                if (!mCustomProjMatrix)
363                {
364
365                        // The code below will dealing with general projection
366                        // parameters, similar glFrustum and glOrtho.
367                        // Doesn't optimise manually except division operator, so the
368                        // code more self-explaining.
369
370                        Real inv_w = 1 / (right - left);
371                        Real inv_h = 1 / (top - bottom);
372                        Real inv_d = 1 / (mFarDist - mNearDist);
373
374                        // Recalc if frustum params changed
375                        if (mProjType == PT_PERSPECTIVE)
376                        {
377                                // Calc matrix elements
378                                Real A = 2 * mNearDist * inv_w;
379                                Real B = 2 * mNearDist * inv_h;
380                                Real C = (right + left) * inv_w;
381                                Real D = (top + bottom) * inv_h;
382                                Real q, qn;
383                                if (mFarDist == 0)
384                                {
385                                        // Infinite far plane
386                                        q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
387                                        qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2);
388                                }
389                                else
390                                {
391                                        q = - (mFarDist + mNearDist) * inv_d;
392                                        qn = -2 * (mFarDist * mNearDist) * inv_d;
393                                }
394
395                                // NB: This creates 'uniform' perspective projection matrix,
396                                // which depth range [-1,1], right-handed rules
397                                //
398                                // [ A   0   C   0  ]
399                                // [ 0   B   D   0  ]
400                                // [ 0   0   q   qn ]
401                                // [ 0   0   -1  0  ]
402                                //
403                                // A = 2 * near / (right - left)
404                                // B = 2 * near / (top - bottom)
405                                // C = (right + left) / (right - left)
406                                // D = (top + bottom) / (top - bottom)
407                                // q = - (far + near) / (far - near)
408                                // qn = - 2 * (far * near) / (far - near)
409
410                                mProjMatrix = Matrix4::ZERO;
411                                mProjMatrix[0][0] = A;
412                                mProjMatrix[0][2] = C;
413                                mProjMatrix[1][1] = B;
414                                mProjMatrix[1][2] = D;
415                                mProjMatrix[2][2] = q;
416                                mProjMatrix[2][3] = qn;
417                                mProjMatrix[3][2] = -1;
418
419                                if (mObliqueDepthProjection)
420                                {
421                                        // Translate the plane into view space
422
423                                        // Don't use getViewMatrix here, incase overrided by
424                                        // camera and return a cull frustum view matrix
425                                        updateView();
426                                        Plane plane = mViewMatrix * mObliqueProjPlane;
427
428                                        // Thanks to Eric Lenyel for posting this calculation
429                                        // at www.terathon.com
430
431                                        // Calculate the clip-space corner point opposite the
432                                        // clipping plane
433                                        // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
434                                        // transform it into camera space by multiplying it
435                                        // by the inverse of the projection matrix
436
437                                        /* generalised version
438                                        Vector4 q = matrix.inverse() *
439                                        Vector4(Math::Sign(plane.normal.x),
440                                        Math::Sign(plane.normal.y), 1.0f, 1.0f);
441                                        */
442                                        Vector4 q;
443                                        q.x = (Math::Sign(plane.normal.x) + mProjMatrix[0][2]) / mProjMatrix[0][0];
444                                        q.y = (Math::Sign(plane.normal.y) + mProjMatrix[1][2]) / mProjMatrix[1][1];
445                                        q.z = -1;
446                                        q.w = (1 + mProjMatrix[2][2]) / mProjMatrix[2][3];
447
448                                        // Calculate the scaled plane vector
449                                        Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d);
450                                        Vector4 c = clipPlane4d * (2 / (clipPlane4d.dotProduct(q)));
451
452                                        // Replace the third row of the projection matrix
453                                        mProjMatrix[2][0] = c.x;
454                                        mProjMatrix[2][1] = c.y;
455                                        mProjMatrix[2][2] = c.z + 1;
456                                        mProjMatrix[2][3] = c.w;
457                                }
458                        } // perspective
459                        else if (mProjType == PT_ORTHOGRAPHIC)
460                        {
461                                Real A = 2 * inv_w;
462                                Real B = 2 * inv_h;
463                                Real C = - (right + left) * inv_w;
464                                Real D = - (top + bottom) * inv_h;
465                                Real q, qn;
466                                if (mFarDist == 0)
467                                {
468                                        // Can not do infinite far plane here, avoid divided zero only
469                                        q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist;
470                                        qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
471                                }
472                                else
473                                {
474                                        q = - 2 * inv_d;
475                                        qn = - (mFarDist + mNearDist)  * inv_d;
476                                }
477
478                                // NB: This creates 'uniform' orthographic projection matrix,
479                                // which depth range [-1,1], right-handed rules
480                                //
481                                // [ A   0   0   C  ]
482                                // [ 0   B   0   D  ]
483                                // [ 0   0   q   qn ]
484                                // [ 0   0   0   1  ]
485                                //
486                                // A = 2 * / (right - left)
487                                // B = 2 * / (top - bottom)
488                                // C = - (right + left) / (right - left)
489                                // D = - (top + bottom) / (top - bottom)
490                                // q = - 2 / (far - near)
491                                // qn = - (far + near) / (far - near)
492
493                                mProjMatrix = Matrix4::ZERO;
494                                mProjMatrix[0][0] = A;
495                                mProjMatrix[0][3] = C;
496                                mProjMatrix[1][1] = B;
497                                mProjMatrix[1][3] = D;
498                                mProjMatrix[2][2] = q;
499                                mProjMatrix[2][3] = qn;
500                                mProjMatrix[3][3] = 1;
501                        } // ortho
502                } // !mCustomProjMatrix
503
504                RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
505                // API specific
506                renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS);
507                // API specific for Gpu Programs
508                renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true);
509
510
511                // Calculate bounding box (local)
512                // Box is from 0, down -Z, max dimensions as determined from far plane
513                // If infinite view frustum just pick a far value
514                Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
515                // Near plane bounds
516                Vector3 min(left, bottom, -farDist);
517                Vector3 max(right, top, 0);
518                if (mProjType == PT_PERSPECTIVE)
519                {
520                        // Merge with far plane bounds
521                        Real radio = farDist / mNearDist;
522                        min.makeFloor(Vector3(left * radio, bottom * radio, -farDist));
523                        max.makeCeil(Vector3(right * radio, top * radio, 0));
524                }
525                mBoundingBox.setExtents(min, max);
526
527                mRecalcFrustum = false;
528
529                // Signal to update frustum clipping planes
530                mRecalcFrustumPlanes = true;
531        }
532    //-----------------------------------------------------------------------
533    void Frustum::updateFrustum(void) const
534    {
535        if (isFrustumOutOfDate())
536        {
537                        updateFrustumImpl();
538        }
539    }
540
541    //-----------------------------------------------------------------------
542    void Frustum::updateVertexData(void) const
543    {
544        if (mRecalcVertexData)
545        {
546            if (mVertexData.vertexBufferBinding->getBufferCount() <= 0)
547            {
548                // Initialise vertex & index data
549                mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION);
550                mVertexData.vertexCount = 32;
551                mVertexData.vertexStart = 0;
552                mVertexData.vertexBufferBinding->setBinding( 0,
553                    HardwareBufferManager::getSingleton().createVertexBuffer(
554                        sizeof(float)*3, 32, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) );
555            }
556
557            // Note: Even though we can dealing with general projection matrix here,
558            //       but because it's incompatibly with infinite far plane, thus, we
559            //       still need to working with projection parameters.
560
561            // Calc near plane corners
562            Real vpLeft, vpRight, vpBottom, vpTop;
563            calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop);
564
565            // Treat infinite fardist as some arbitrary far value
566            Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
567
568            // Calc far palne corners
569            Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
570            Real farLeft = vpLeft * radio;
571            Real farRight = vpRight * radio;
572            Real farBottom = vpBottom * radio;
573            Real farTop = vpTop * radio;
574
575            // Calculate vertex positions (local)
576            // 0 is the origin
577            // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise
578            // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise
579            HardwareVertexBufferSharedPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0);
580            float* pFloat = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
581
582            // near plane (remember frustum is going in -Z direction)
583            *pFloat++ = vpLeft;  *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
584            *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
585
586            *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
587            *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
588
589            *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
590            *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
591
592            *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
593            *pFloat++ = vpLeft;  *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
594
595            // far plane (remember frustum is going in -Z direction)
596            *pFloat++ = farLeft;  *pFloat++ = farTop;    *pFloat++ = -farDist;
597            *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;
598
599            *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;
600            *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
601
602            *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
603            *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;
604
605            *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;
606            *pFloat++ = farLeft;  *pFloat++ = farTop;    *pFloat++ = -farDist;
607
608            // Sides of the pyramid
609            *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
610            *pFloat++ = vpLeft;  *pFloat++ = vpTop;  *pFloat++ = -mNearDist;
611
612            *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
613            *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
614
615            *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
616            *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
617
618            *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
619            *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
620
621            // Sides of the box
622
623            *pFloat++ = vpLeft;  *pFloat++ = vpTop;  *pFloat++ = -mNearDist;
624            *pFloat++ = farLeft;  *pFloat++ = farTop;  *pFloat++ = -farDist;
625
626            *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
627            *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;
628
629            *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
630            *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
631
632            *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
633            *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;
634
635
636            vbuf->unlock();
637
638            mRecalcVertexData = false;
639        }
640    }
641
642    //-----------------------------------------------------------------------
643    bool Frustum::isViewOutOfDate(void) const
644    {
645        // Attached to node?
646        if (mParentNode)
647        {
648            if (mRecalcView ||
649                mParentNode->_getDerivedOrientation() != mLastParentOrientation ||
650                mParentNode->_getDerivedPosition() != mLastParentPosition)
651            {
652                // Ok, we're out of date with SceneNode we're attached to
653                mLastParentOrientation = mParentNode->_getDerivedOrientation();
654                mLastParentPosition = mParentNode->_getDerivedPosition();
655                mRecalcView = true;
656            }
657        }
658        // Deriving reflection from linked plane?
659        if (mLinkedReflectPlane &&
660            !(mLastLinkedReflectionPlane == mLinkedReflectPlane->_getDerivedPlane()))
661        {
662            mReflectPlane = mLinkedReflectPlane->_getDerivedPlane();
663            mReflectMatrix = Math::buildReflectionMatrix(mReflectPlane);
664            mLastLinkedReflectionPlane = mLinkedReflectPlane->_getDerivedPlane();
665            mRecalcView = true;
666        }
667
668        return mRecalcView;
669    }
670
671    //-----------------------------------------------------------------------
672    bool Frustum::isFrustumOutOfDate(void) const
673    {
674        // Deriving custom near plane from linked plane?
675        if (mObliqueDepthProjection)
676        {
677            // Out of date when view out of data since plane needs to be in view space
678            if (isViewOutOfDate())
679            {
680                mRecalcFrustum = true;
681            }
682            // Update derived plane
683            if (mLinkedObliqueProjPlane &&
684                !(mLastLinkedObliqueProjPlane == mLinkedObliqueProjPlane->_getDerivedPlane()))
685            {
686                mObliqueProjPlane = mLinkedObliqueProjPlane->_getDerivedPlane();
687                mLastLinkedObliqueProjPlane = mObliqueProjPlane;
688                mRecalcFrustum = true;
689            }
690        }
691
692        return mRecalcFrustum;
693    }
694
695    //-----------------------------------------------------------------------
696        void Frustum::updateViewImpl(void) const
697        {
698                // ----------------------
699                // Update the view matrix
700                // ----------------------
701
702                // View matrix is:
703                //
704                //  [ Lx  Uy  Dz  Tx  ]
705                //  [ Lx  Uy  Dz  Ty  ]
706                //  [ Lx  Uy  Dz  Tz  ]
707                //  [ 0   0   0   1   ]
708                //
709                // Where T = -(Transposed(Rot) * Pos)
710
711                // This is most efficiently done using 3x3 Matrices
712
713                // Get orientation from quaternion
714
715                if (!mCustomViewMatrix)
716                {
717                        Matrix3 rot;
718                        const Quaternion& orientation = getOrientationForViewUpdate();
719                        const Vector3& position = getPositionForViewUpdate();
720                        orientation.ToRotationMatrix(rot);
721
722                        // Make the translation relative to new axes
723                        Matrix3 rotT = rot.Transpose();
724                        Vector3 trans = -rotT * position;
725
726                        // Make final matrix
727                        mViewMatrix = Matrix4::IDENTITY;
728                        mViewMatrix = rotT; // fills upper 3x3
729                        mViewMatrix[0][3] = trans.x;
730                        mViewMatrix[1][3] = trans.y;
731                        mViewMatrix[2][3] = trans.z;
732
733                        // Deal with reflections
734                        if (mReflect)
735                        {
736                                mViewMatrix = mViewMatrix * mReflectMatrix;
737                        }
738                }
739
740                mRecalcView = false;
741
742                // Signal to update frustum clipping planes
743                mRecalcFrustumPlanes = true;
744                // Signal to update world space corners
745                mRecalcWorldSpaceCorners = true;
746                // Signal to update frustum if oblique plane enabled,
747                // since plane needs to be in view space
748                if (mObliqueDepthProjection)
749                {
750                        mRecalcFrustum = true;
751                }
752        }
753        //-----------------------------------------------------------------------
754    void Frustum::updateView(void) const
755    {
756        if (isViewOutOfDate())
757        {
758                        updateViewImpl();
759        }
760    }
761
762        //-----------------------------------------------------------------------
763        void Frustum::updateFrustumPlanesImpl(void) const
764        {
765                // -------------------------
766                // Update the frustum planes
767                // -------------------------
768                Matrix4 combo = mProjMatrix * mViewMatrix;
769
770                mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0];
771                mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1];
772                mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2];
773                mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3];
774
775                mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0];
776                mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1];
777                mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2];
778                mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3];
779
780                mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0];
781                mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1];
782                mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2];
783                mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3];
784
785                mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0];
786                mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1];
787                mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2];
788                mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3];
789
790                mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0];
791                mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1];
792                mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2];
793                mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3];
794
795                mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0];
796                mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1];
797                mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2];
798                mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3];
799
800                // Renormalise any normals which were not unit length
801                for(int i=0; i<6; i++ )
802                {
803                        float length = mFrustumPlanes[i].normal.normalise();
804                        mFrustumPlanes[i].d /= length;
805                }
806
807                mRecalcFrustumPlanes = false;
808        }
809    //-----------------------------------------------------------------------
810    void Frustum::updateFrustumPlanes(void) const
811    {
812        updateView();
813        updateFrustum();
814
815        if (mRecalcFrustumPlanes)
816        {
817                        updateFrustumPlanesImpl();
818        }
819    }
820        //-----------------------------------------------------------------------
821        void Frustum::updateWorldSpaceCornersImpl(void) const
822        {
823                Matrix4 eyeToWorld = mViewMatrix.inverse();
824
825                // Note: Even though we can dealing with general projection matrix here,
826                //       but because it's incompatibly with infinite far plane, thus, we
827                //       still need to working with projection parameters.
828
829                // Calc near plane corners
830                Real nearLeft, nearRight, nearBottom, nearTop;
831                calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop);
832
833                // Treat infinite fardist as some arbitrary far value
834                Real farDist = (mFarDist == 0) ? 100000 : mFarDist;
835
836                // Calc far palne corners
837                Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
838                Real farLeft = nearLeft * radio;
839                Real farRight = nearRight * radio;
840                Real farBottom = nearBottom * radio;
841                Real farTop = nearTop * radio;
842
843                // near
844                mWorldSpaceCorners[0] = eyeToWorld * Vector3(nearRight, nearTop,    -mNearDist);
845                mWorldSpaceCorners[1] = eyeToWorld * Vector3(nearLeft,  nearTop,    -mNearDist);
846                mWorldSpaceCorners[2] = eyeToWorld * Vector3(nearLeft,  nearBottom, -mNearDist);
847                mWorldSpaceCorners[3] = eyeToWorld * Vector3(nearRight, nearBottom, -mNearDist);
848                // far
849                mWorldSpaceCorners[4] = eyeToWorld * Vector3(farRight,  farTop,     -farDist);
850                mWorldSpaceCorners[5] = eyeToWorld * Vector3(farLeft,   farTop,     -farDist);
851                mWorldSpaceCorners[6] = eyeToWorld * Vector3(farLeft,   farBottom,  -farDist);
852                mWorldSpaceCorners[7] = eyeToWorld * Vector3(farRight,  farBottom,  -farDist);
853
854
855                mRecalcWorldSpaceCorners = false;
856        }
857    //-----------------------------------------------------------------------
858    void Frustum::updateWorldSpaceCorners(void) const
859    {
860        updateView();
861
862        if (mRecalcWorldSpaceCorners)
863        {
864                        updateWorldSpaceCornersImpl();
865        }
866
867    }
868
869    //-----------------------------------------------------------------------
870    Real Frustum::getAspectRatio(void) const
871    {
872        return mAspect;
873    }
874
875    //-----------------------------------------------------------------------
876    void Frustum::setAspectRatio(Real r)
877    {
878        mAspect = r;
879        invalidateFrustum();
880    }
881
882    //-----------------------------------------------------------------------
883    const AxisAlignedBox& Frustum::getBoundingBox(void) const
884    {
885        return mBoundingBox;
886    }
887    //-----------------------------------------------------------------------
888    void Frustum::_updateRenderQueue(RenderQueue* queue)
889    {
890        // Add self
891        queue->addRenderable(this);
892    }
893    //-----------------------------------------------------------------------
894    const String& Frustum::getMovableType(void) const
895    {
896        return msMovableType;
897    }
898    //-----------------------------------------------------------------------
899        Real Frustum::getBoundingRadius(void) const
900        {
901        return (mFarDist == 0)? 100000 : mFarDist;
902        }
903    //-----------------------------------------------------------------------
904    const MaterialPtr& Frustum::getMaterial(void) const
905    {
906        return mMaterial;
907    }
908    //-----------------------------------------------------------------------
909    void Frustum::getRenderOperation(RenderOperation& op)
910    {
911        updateVertexData();
912        op.operationType = RenderOperation::OT_LINE_LIST;
913        op.useIndexes = false;
914        op.vertexData = &mVertexData;
915    }
916    //-----------------------------------------------------------------------
917    void Frustum::getWorldTransforms(Matrix4* xform) const
918    {
919        if (mParentNode)
920            mParentNode->getWorldTransforms(xform);
921    }
922    //-----------------------------------------------------------------------
923    const Quaternion& Frustum::getWorldOrientation(void) const
924    {
925        if (mParentNode)
926            return mParentNode->_getDerivedOrientation();
927        else
928            return Quaternion::IDENTITY;
929    }
930    //-----------------------------------------------------------------------
931    const Vector3& Frustum::getWorldPosition(void) const
932    {
933        if (mParentNode)
934            return mParentNode->_getDerivedPosition();
935        else
936            return Vector3::ZERO;
937    }
938    //-----------------------------------------------------------------------
939    Real Frustum::getSquaredViewDepth(const Camera* cam) const
940    {
941        // Calc from centre
942        if (mParentNode)
943            return (cam->getDerivedPosition()
944                - mParentNode->_getDerivedPosition()).squaredLength();
945        else
946            return 0;
947    }
948    //-----------------------------------------------------------------------
949    const LightList& Frustum::getLights(void) const
950    {
951        // N/A
952        static LightList ll;
953        return ll;
954    }
955    //-----------------------------------------------------------------------
956    void Frustum::_notifyCurrentCamera(Camera* cam)
957    {
958        // Make sure bounding box up-to-date
959        updateFrustum();
960
961        MovableObject::_notifyCurrentCamera(cam);
962    }
963
964    // -------------------------------------------------------------------
965    void Frustum::invalidateFrustum() const
966    {
967        mRecalcFrustum = true;
968        mRecalcFrustumPlanes = true;
969        mRecalcWorldSpaceCorners = true;
970        mRecalcVertexData = true;
971    }
972    // -------------------------------------------------------------------
973    void Frustum::invalidateView() const
974    {
975        mRecalcView = true;
976        mRecalcFrustumPlanes = true;
977        mRecalcWorldSpaceCorners = true;
978    }
979    // -------------------------------------------------------------------
980    const Vector3* Frustum::getWorldSpaceCorners(void) const
981    {
982        updateWorldSpaceCorners();
983
984        return mWorldSpaceCorners;
985    }
986    //-----------------------------------------------------------------------
987    void Frustum::setProjectionType(ProjectionType pt)
988    {
989        mProjType = pt;
990        invalidateFrustum();
991    }
992
993    //-----------------------------------------------------------------------
994    ProjectionType Frustum::getProjectionType(void) const
995    {
996        return mProjType;
997    }
998    //-----------------------------------------------------------------------
999    const Vector3& Frustum::getPositionForViewUpdate(void) const
1000    {
1001        return mLastParentPosition;
1002    }
1003    //-----------------------------------------------------------------------
1004    const Quaternion& Frustum::getOrientationForViewUpdate(void) const
1005    {
1006        return mLastParentOrientation;
1007    }
1008    //-----------------------------------------------------------------------
1009    void Frustum::enableReflection(const Plane& p)
1010    {
1011        mReflect = true;
1012        mReflectPlane = p;
1013        mLinkedReflectPlane = 0;
1014        mReflectMatrix = Math::buildReflectionMatrix(p);
1015        invalidateView();
1016
1017    }
1018    //-----------------------------------------------------------------------
1019    void Frustum::enableReflection(const MovablePlane* p)
1020    {
1021        mReflect = true;
1022        mLinkedReflectPlane = p;
1023        mReflectPlane = mLinkedReflectPlane->_getDerivedPlane();
1024        mReflectMatrix = Math::buildReflectionMatrix(mReflectPlane);
1025        mLastLinkedReflectionPlane = mLinkedReflectPlane->_getDerivedPlane();
1026        invalidateView();
1027    }
1028    //-----------------------------------------------------------------------
1029    void Frustum::disableReflection(void)
1030    {
1031        mReflect = false;
1032        mLinkedReflectPlane = 0;
1033        mLastLinkedReflectionPlane.normal = Vector3::ZERO;
1034        invalidateView();
1035    }
1036    //---------------------------------------------------------------------
1037    bool Frustum::projectSphere(const Sphere& sphere,
1038        Real* left, Real* top, Real* right, Real* bottom) const
1039    {
1040        // initialise
1041        *left = *bottom = -1.0f;
1042        *right = *top = 1.0f;
1043
1044        // Transform light position into camera space
1045
1046        // Don't use getViewMatrix here, incase overrided by camera and return a cull frustum view matrix
1047        updateView();
1048        Vector3 eyeSpacePos = mViewMatrix * sphere.getCenter();
1049
1050        if (eyeSpacePos.z < 0)
1051        {
1052            Real r = sphere.getRadius();
1053            // early-exit
1054            if (eyeSpacePos.squaredLength() <= r * r)
1055                return false;
1056
1057            updateFrustum();
1058            Vector3 screenSpacePos = mProjMatrix * eyeSpacePos;
1059
1060
1061            // perspective attenuate
1062            Vector3 spheresize(r, r, eyeSpacePos.z);
1063            spheresize = mProjMatrix * spheresize;
1064
1065            Real possLeft = screenSpacePos.x - spheresize.x;
1066            Real possRight = screenSpacePos.x + spheresize.x;
1067            Real possTop = screenSpacePos.y + spheresize.y;
1068            Real possBottom = screenSpacePos.y - spheresize.y;
1069
1070            *left = std::max(static_cast<Real>(-1.0), possLeft);
1071            *right = std::min(static_cast<Real>(1.0), possRight);
1072            *top = std::min(static_cast<Real>(1.0), possTop);
1073            *bottom = std::max(static_cast<Real>(-1.0), possBottom);
1074
1075        }
1076
1077        return (*left != -1.0f) || (*top != 1.0f) || (*right != 1.0f) || (*bottom != -1.0f);
1078
1079    }
1080    //---------------------------------------------------------------------
1081    void Frustum::enableCustomNearClipPlane(const MovablePlane* plane)
1082    {
1083        mObliqueDepthProjection = true;
1084        mLinkedObliqueProjPlane = plane;
1085        mObliqueProjPlane = plane->_getDerivedPlane();
1086        invalidateFrustum();
1087    }
1088    //---------------------------------------------------------------------
1089    void Frustum::enableCustomNearClipPlane(const Plane& plane)
1090    {
1091        mObliqueDepthProjection = true;
1092        mLinkedObliqueProjPlane = 0;
1093        mObliqueProjPlane = plane;
1094        invalidateFrustum();
1095    }
1096    //---------------------------------------------------------------------
1097    void Frustum::disableCustomNearClipPlane(void)
1098    {
1099        mObliqueDepthProjection = false;
1100        mLinkedObliqueProjPlane = 0;
1101        invalidateFrustum();
1102    }
1103    //---------------------------------------------------------------------
1104        void Frustum::setCustomViewMatrix(bool enable, const Matrix4& viewMatrix)
1105        {
1106                mCustomViewMatrix = enable;
1107                if (enable)
1108                {
1109                        mViewMatrix = viewMatrix;
1110                }
1111                invalidateView();
1112        }
1113    //---------------------------------------------------------------------
1114        void Frustum::setCustomProjectionMatrix(bool enable, const Matrix4& projMatrix)
1115        {
1116                mCustomProjMatrix = enable;
1117                if (enable)
1118                {
1119                        mProjMatrix = projMatrix;
1120                }
1121                invalidateFrustum();
1122        }
1123
1124#ifdef GTP_VISIBILITY_MODIFIED_OGRE
1125    //-----------------------------------------------------------------------
1126        //added by matt: 050405
1127    bool Frustum::isVisible(const AxisAlignedBox& bound, bool &intersects, FrustumPlane* culledBy) const
1128        {
1129                // default: box does not intersect frustum
1130                intersects = false;
1131
1132                // Null boxes always invisible
1133        if (bound.isNull()) return false;
1134
1135        // Make any pending updates to the calculated frustum planes
1136        updateFrustumPlanes();
1137
1138        // Get corners of the box
1139        const Vector3* pCorners = bound.getAllCorners();
1140
1141        // For each plane, see if all points are on the negative side
1142        // If so, object is not visible
1143        for (int plane = 0; plane < 6; ++ plane)
1144        {
1145            // Skip far plane if infinite view frustum
1146            if (mFarDist == 0 && plane == FRUSTUM_PLANE_FAR)
1147                continue;
1148                       
1149                        int corner = 0;
1150                        bool visible = false;
1151
1152                        while ((corner < 8) && (!visible || ((plane == FRUSTUM_PLANE_NEAR) && !intersects)))
1153                        {
1154                                if (mFrustumPlanes[plane].getSide(pCorners[corner ++]) == Plane::NEGATIVE_SIDE)
1155                                {
1156                                        if (plane == FRUSTUM_PLANE_NEAR)
1157                                                intersects = true;
1158                                }
1159                                else
1160                                {
1161                                        visible = true;
1162                                }
1163                        }
1164           
1165                        if (!visible)
1166                        {
1167                                // ALL corners on negative side therefore out of view
1168                                if (culledBy)
1169                                        *culledBy = (FrustumPlane)plane;
1170                                return false;
1171            }
1172                }
1173
1174        return true;
1175    }
1176#endif // GTP_VISIBILITY_MODIFIED_OGRE
1177
1178} // namespace Ogre
Note: See TracBrowser for help on using the repository browser.