source: NonGTP/OpenEXR/include/Imath/ImathFrustum.h @ 855

Revision 855, 17.3 KB checked in by igarcia, 18 years ago (diff)
Line 
1///////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
4// Digital Ltd. LLC
5//
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11// *       Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// *       Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// *       Neither the name of Industrial Light & Magic nor the names of
18// its contributors may be used to endorse or promote products derived
19// from this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//
33///////////////////////////////////////////////////////////////////////////
34
35
36
37#ifndef INCLUDED_IMATHFRUSTUM_H
38#define INCLUDED_IMATHFRUSTUM_H
39
40
41#include <ImathVec.h>
42#include <ImathPlane.h>
43#include <ImathLine.h>
44#include <ImathMatrix.h>
45#include <ImathLimits.h>
46#include <ImathFun.h>
47#include <IexMathExc.h>
48
49namespace Imath {
50
51//
52//      template class Frustum<T>
53//
54//      The frustum is always located with the eye point at the
55//      origin facing down -Z. This makes the Frustum class
56//      compatable with OpenGL (or anything that assumes a camera
57//      looks down -Z, hence with a right-handed coordinate system)
58//      but not with RenderMan which assumes the camera looks down
59//      +Z. Additional functions are provided for conversion from
60//      and from various camera coordinate spaces.
61//
62
63
64template<class T>
65class Frustum
66{
67  public:
68    Frustum();
69    Frustum(const Frustum &);
70    Frustum(T near, T far, T left, T right, T top, T bottom, bool ortho=false);
71    Frustum(T near, T far, T fovx, T fovy, T aspect);
72    virtual ~Frustum();
73
74    //--------------------
75    // Assignment operator
76    //--------------------
77
78    const Frustum &operator     = (const Frustum &);
79
80    //--------------------------------------------------------
81    //  Set functions change the entire state of the Frustum
82    //--------------------------------------------------------
83
84    void                set(T near, T far,
85                            T left, T right,
86                            T top, T bottom,
87                            bool ortho=false);
88
89    void                set(T near, T far, T fovx, T fovy, T aspect);
90
91    //------------------------------------------------------
92    //  These functions modify an already valid frustum state
93    //------------------------------------------------------
94
95    void                modifyNearAndFar(T near, T far);
96    void                setOrthographic(bool);
97
98    //--------------
99    //  Access
100    //--------------
101
102    bool                orthographic() const    { return _orthographic; }
103    T                   near() const            { return _near;         }
104    T                   far() const             { return _far;          }
105    T                   left() const            { return _left;         }
106    T                   right() const           { return _right;        }
107    T                   bottom() const          { return _bottom;       }
108    T                   top() const             { return _top;          }
109
110    //-----------------------------------------------------------------------
111    //  Sets the planes in p to be the six bounding planes of the frustum, in
112    //  the following order: top, right, bottom, left, near, far.
113    //  Note that the planes have normals that point out of the frustum.
114    //  The version of this routine that takes a matrix applies that matrix
115    //  to transform the frustum before setting the planes.
116    //-----------------------------------------------------------------------
117
118    void                planes(Plane3<T> p[6]);
119    void                planes(Plane3<T> p[6], const Matrix44<T> &M);
120
121    //----------------------
122    //  Derived Quantities
123    //----------------------
124
125    T                   fovx() const;
126    T                   fovy() const;
127    T                   aspect() const;
128    Matrix44<T>         projectionMatrix() const;
129
130    //-----------------------------------------------------------------------
131    //  Takes a rectangle in the screen space (i.e., -1 <= left <= right <= 1
132    //  and -1 <= bottom <= top <= 1) of this Frustum, and returns a new
133    //  Frustum whose near clipping-plane window is that rectangle in local
134    //  space. 
135    //-----------------------------------------------------------------------
136
137    Frustum<T>          window(T left, T right, T top, T bottom) const;
138
139    //----------------------------------------------------------
140    // Projection is in screen space / Conversion from Z-Buffer
141    //----------------------------------------------------------
142
143    Line3<T>            projectScreenToRay( const Vec2<T> & ) const;
144    Vec2<T>             projectPointToScreen( const Vec3<T> & ) const;
145
146    T                   ZToDepth(long zval, long min, long max) const;
147    T                   normalizedZToDepth(T zval) const;
148    long                DepthToZ(T depth, long zmin, long zmax) const;
149
150    T                   worldRadius(const Vec3<T> &p, T radius) const;
151    T                   screenRadius(const Vec3<T> &p, T radius) const;
152
153
154  protected:
155
156    Vec2<T>             screenToLocal( const Vec2<T> & ) const;
157    Vec2<T>             localToScreen( const Vec2<T> & ) const;
158
159  protected:
160    T                   _near;
161    T                   _far;
162    T                   _left;
163    T                   _right;
164    T                   _top;
165    T                   _bottom;
166    bool                _orthographic;
167};
168
169
170template<class T>
171inline Frustum<T>::Frustum()
172{
173    set(T (0.1),
174        T (1000.0),
175        T (-1.0),
176        T (1.0),
177        T (1.0),
178        T (-1.0),
179        false);
180}
181
182template<class T>
183inline Frustum<T>::Frustum(const Frustum &f)
184{
185    *this = f;
186}
187
188template<class T>
189inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)
190{
191    set(n,f,l,r,t,b,o);
192}
193
194template<class T>
195inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect)
196{
197    set(near,far,fovx,fovy,aspect);
198}
199
200template<class T>
201Frustum<T>::~Frustum()
202{
203}
204
205template<class T>
206const Frustum<T> &
207Frustum<T>::operator = (const Frustum &f)
208{
209    _near         = f._near;
210    _far          = f._far;
211    _left         = f._left;
212    _right        = f._right;
213    _top          = f._top;
214    _bottom       = f._bottom;
215    _orthographic = f._orthographic;
216
217    return *this;
218}
219
220template<class T>
221void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)
222{
223    _near           = n;
224    _far            = f;
225    _left           = l;
226    _right          = r;
227    _bottom         = b;
228    _top            = t;
229    _orthographic   = o;
230}
231
232template<class T>
233void Frustum<T>::modifyNearAndFar(T n, T f)
234{
235    if ( _orthographic )
236    {
237        _near = n;
238    }
239    else
240    {
241        Line3<T>  lowerLeft( Vec3<T>(0,0,0), Vec3<T>(_left,_bottom,-_near) );
242        Line3<T> upperRight( Vec3<T>(0,0,0), Vec3<T>(_right,_top,-_near) );
243        Plane3<T> nearPlane( Vec3<T>(0,0,-1), n );
244
245        Vec3<T> ll,ur;
246        nearPlane.intersect(lowerLeft,ll);
247        nearPlane.intersect(upperRight,ur);
248
249        _left   = ll.x;
250        _right  = ur.x;
251        _top    = ur.y;
252        _bottom = ll.y;
253        _near   = n;
254        _far    = f;
255    }
256
257    _far = f;
258}
259
260template<class T>
261void Frustum<T>::setOrthographic(bool ortho)
262{
263    _orthographic   = ortho;
264}
265
266template<class T>
267void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect)
268{
269    if (fovx != 0 && fovy != 0)
270        throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");
271
272    if (fovx != 0)
273    {
274        _right      = near * Math<T>::tan(fovx/2.0);
275        _left       = -_right;
276        _top        = ((_right - _left)/aspect)/2.0;
277        _bottom     = -_top;
278    }
279    else
280    {
281        _top        = near * Math<T>::tan(fovy/2.0);
282        _bottom     = -_top;
283        _right      = (_top - _bottom) * aspect / 2.0;
284        _left       = -_right;
285    }
286    _near           = near;
287    _far            = far;
288    _orthographic   = false;
289}
290
291template<class T>
292T Frustum<T>::fovx() const
293{
294    return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near);
295}
296
297template<class T>
298T Frustum<T>::fovy() const
299{
300    return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near);
301}
302
303template<class T>
304T Frustum<T>::aspect() const
305{
306    T rightMinusLeft = _right-_left;
307    T topMinusBottom = _top-_bottom;
308
309    if (abs(topMinusBottom) < 1 &&
310        abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))
311    {
312        throw Iex::DivzeroExc ("Bad viewing frustum: "
313                               "aspect ratio cannot be computed.");
314    }
315
316    return rightMinusLeft / topMinusBottom;
317}
318
319template<class T>
320Matrix44<T> Frustum<T>::projectionMatrix() const
321{
322    T rightPlusLeft  = _right+_left;
323    T rightMinusLeft = _right-_left;
324
325    T topPlusBottom  = _top+_bottom;
326    T topMinusBottom = _top-_bottom;
327
328    T farPlusNear    = _far+_near;
329    T farMinusNear   = _far-_near;
330
331    if ((abs(rightMinusLeft) < 1 &&
332         abs(rightPlusLeft) > limits<T>::max() * abs(rightMinusLeft)) ||
333        (abs(topMinusBottom) < 1 &&
334         abs(topPlusBottom) > limits<T>::max() * abs(topMinusBottom)) ||
335        (abs(farMinusNear) < 1 &&
336         abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)))
337    {
338        throw Iex::DivzeroExc ("Bad viewing frustum: "
339                               "projection matrix cannot be computed.");
340    }
341
342    if ( _orthographic )
343    {
344        T tx = -rightPlusLeft / rightMinusLeft;
345        T ty = -topPlusBottom / topMinusBottom;
346        T tz = -farPlusNear   / farMinusNear;
347
348        if ((abs(rightMinusLeft) < 1 &&
349             2 > limits<T>::max() * abs(rightMinusLeft)) ||
350            (abs(topMinusBottom) < 1 &&
351             2 > limits<T>::max() * abs(topMinusBottom)) ||
352            (abs(farMinusNear) < 1 &&
353             2 > limits<T>::max() * abs(farMinusNear)))
354        {
355            throw Iex::DivzeroExc ("Bad viewing frustum: "
356                                   "projection matrix cannot be computed.");
357        }
358
359        T A  =  2 / rightMinusLeft;
360        T B  =  2 / topMinusBottom;
361        T C  = -2 / farMinusNear;
362
363        return Matrix44<T>( A,  0,  0,  0,
364                            0,  B,  0,  0,
365                            0,  0,  C,  0,
366                            tx, ty, tz, 1.f );
367    }
368    else
369    {
370        T A =  rightPlusLeft / rightMinusLeft;
371        T B =  topPlusBottom / topMinusBottom;
372        T C = -farPlusNear   / farMinusNear;
373
374        T farTimesNear = -2 * _far * _near;
375        if (abs(farMinusNear) < 1 &&
376            abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
377        {
378            throw Iex::DivzeroExc ("Bad viewing frustum: "
379                                   "projection matrix cannot be computed.");
380        }
381
382        T D = farTimesNear / farMinusNear;
383
384        T twoTimesNear = 2 * _near;
385
386        if ((abs(rightMinusLeft) < 1 &&
387             abs(twoTimesNear) > limits<T>::max() * abs(rightMinusLeft)) ||
388            (abs(topMinusBottom) < 1 &&
389             abs(twoTimesNear) > limits<T>::max() * abs(topMinusBottom)))
390        {
391            throw Iex::DivzeroExc ("Bad viewing frustum: "
392                                   "projection matrix cannot be computed.");
393        }
394
395        T E = twoTimesNear / rightMinusLeft;
396        T F = twoTimesNear / topMinusBottom;
397
398        return Matrix44<T>( E,  0,  0,  0,
399                            0,  F,  0,  0,
400                            A,  B,  C, -1,
401                            0,  0,  D,  0 );
402    }
403}
404
405template<class T>
406Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const
407{
408    // move it to 0->1 space
409
410    Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );
411    Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );
412
413    return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic);
414}
415
416
417template<class T>
418Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const
419{
420    return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,
421                    _bottom + (_top-_bottom) * (1.f+s.y) / 2.f );
422}
423
424template<class T>
425Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const
426{
427    T leftPlusRight  = _left - 2 * p.x + _right;
428    T leftMinusRight = _left-_right;
429    T bottomPlusTop  = _bottom - 2 * p.y + _top;
430    T bottomMinusTop = _bottom-_top;
431
432    if ((abs(leftMinusRight) < 1 &&
433         abs(leftPlusRight) > limits<T>::max() * abs(leftMinusRight)) ||
434        (abs(bottomMinusTop) < 1 &&
435         abs(bottomPlusTop) > limits<T>::max() * abs(bottomMinusTop)))
436    {
437        throw Iex::DivzeroExc
438            ("Bad viewing frustum: "
439             "local-to-screen transformation cannot be computed");
440    }
441
442    return Vec2<T>( leftPlusRight / leftMinusRight,
443                    bottomPlusTop / bottomMinusTop );
444}
445
446template<class T>
447Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const
448{
449    Vec2<T> point = screenToLocal(p);
450    if (orthographic())
451        return Line3<T>( Vec3<T>(point.x,point.y, 0.0),
452                         Vec3<T>(point.x,point.y,-_near));
453    else
454        return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near));
455}
456
457template<class T>
458Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const
459{
460    if (orthographic() || point.z == 0)
461        return localToScreen( Vec2<T>( point.x, point.y ) );
462    else
463        return localToScreen( Vec2<T>( point.x * _near / -point.z,
464                                       point.y * _near / -point.z ) );
465}
466
467template<class T>
468T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const
469{
470    int zdiff = zmax - zmin;
471
472    if (zdiff == 0)
473    {
474        throw Iex::DivzeroExc
475            ("Bad call to Frustum::ZToDepth: zmax == zmin");
476    }
477
478    if ( zval > zmax+1 ) zval -= zdiff;
479
480    T fzval = (T(zval) - T(zmin)) / T(zdiff);
481    return normalizedZToDepth(fzval);
482}
483
484template<class T>
485T Frustum<T>::normalizedZToDepth(T zval) const
486{
487    T Zp = zval * 2.0 - 1;
488
489    if ( _orthographic )
490    {
491        return   -(Zp*(_far-_near) + (_far+_near))/2;
492    }
493    else
494    {
495        T farTimesNear = 2 * _far * _near;
496        T farMinusNear = Zp * (_far - _near) - _far - _near;
497
498        if (abs(farMinusNear) < 1 &&
499            abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
500        {
501            throw Iex::DivzeroExc
502                ("Frustum::normalizedZToDepth cannot be computed.  The "
503                 "near and far clipping planes of the viewing frustum "
504                 "may be too close to each other");
505        }
506
507        return farTimesNear / farMinusNear;
508    }
509}
510
511template<class T>
512long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const
513{
514    long zdiff     = zmax - zmin;
515    T farMinusNear = _far-_near;
516
517    if ( _orthographic )
518    {
519        T farPlusNear = 2*depth + _far + _near;
520
521        if (abs(farMinusNear) < 1 &&
522            abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
523        {
524            throw Iex::DivzeroExc
525                ("Bad viewing frustum: near and far clipping planes "
526                 "are too close to each other");
527        }
528
529        T Zp = -farPlusNear/farMinusNear;
530        return long(0.5*(Zp+1)*zdiff) + zmin;
531    }
532    else
533    {
534        // Perspective
535
536        T farTimesNear = 2*_far*_near;
537        if (abs(depth) < 1 &&
538            abs(farTimesNear) > limits<T>::max() * abs(depth))
539        {
540            throw Iex::DivzeroExc
541                ("Bad call to DepthToZ function: value of `depth' "
542                 "is too small");
543        }
544
545        T farPlusNear = farTimesNear/depth + _far + _near;
546        if (abs(farMinusNear) < 1 &&
547            abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
548        {
549            throw Iex::DivzeroExc
550                ("Bad viewing frustum: near and far clipping planes "
551                 "are too close to each other");
552        }
553
554        T Zp = farPlusNear/farMinusNear;
555        return long(0.5*(Zp+1)*zdiff) + zmin;
556    }
557}
558
559template<class T>
560T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const
561{
562    // Derivation:
563    // Consider X-Z plane.
564    // X coord of projection of p = xp = p.x * (-_near / p.z)
565    // Let q be p + (radius, 0, 0).
566    // X coord of projection of q = xq = (p.x - radius)  * (-_near / p.z)
567    // X coord of projection of segment from p to q = r = xp - xq
568    //         = radius * (-_near / p.z)
569    // A similar analysis holds in the Y-Z plane.
570    // So r is the quantity we want to return.
571
572    if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z))
573    {
574        return radius * (-_near / p.z);
575    }
576    else
577    {
578        throw Iex::DivzeroExc
579            ("Bad call to Frustum::screenRadius: the magnitude of `p' "
580             "is too small");
581    }
582
583    return radius * (-_near / p.z);
584}
585
586template<class T>
587T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const
588{
589    if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near))
590    {
591        return radius * (p.z / -_near);
592    }
593    else
594    {
595        throw Iex::DivzeroExc
596            ("Bad viewing frustum: the near clipping plane is too "
597             "close to zero");
598    }
599}
600
601template<class T>
602void Frustum<T>::planes(Plane3<T> p[6])
603{
604    //
605    //  Plane order: Top, Right, Bottom, Left, Near, Far.
606    //  Normals point outwards.
607    //
608
609    Vec3<T> a( _left,  _bottom, -_near);
610    Vec3<T> b( _left,  _top,    -_near);
611    Vec3<T> c( _right, _top,    -_near);
612    Vec3<T> d( _right, _bottom, -_near);
613    Vec3<T> o(0,0,0);
614
615    p[0].set( o, c, b );
616    p[1].set( o, d, c );
617    p[2].set( o, a, d );
618    p[3].set( o, b, a );
619    p[4].set( Vec3<T>(0, 0, 1), -_near );
620    p[5].set( Vec3<T>(0, 0,-1), _far );
621}
622
623
624template<class T>
625void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)
626{
627    //
628    //  Plane order: Top, Right, Bottom, Left, Near, Far.
629    //  Normals point outwards.
630    //
631
632    Vec3<T> a   = Vec3<T>( _left,  _bottom, -_near) * M;
633    Vec3<T> b   = Vec3<T>( _left,  _top,    -_near) * M;
634    Vec3<T> c   = Vec3<T>( _right, _top,    -_near) * M;
635    Vec3<T> d   = Vec3<T>( _right, _bottom, -_near) * M;
636    double s    = _far / double(_near);
637    T farLeft   = (T) (s * _left);
638    T farRight  = (T) (s * _right);
639    T farTop    = (T) (s * _top);
640    T farBottom = (T) (s * _bottom);
641    Vec3<T> e   = Vec3<T>( farLeft,  farBottom, -_far) * M;
642    Vec3<T> f   = Vec3<T>( farLeft,  farTop,    -_far) * M;
643    Vec3<T> g   = Vec3<T>( farRight, farTop,    -_far) * M;
644    Vec3<T> o   = Vec3<T>(0,0,0) * M;
645
646    p[0].set( o, c, b );
647    p[1].set( o, d, c );
648    p[2].set( o, a, d );
649    p[3].set( o, b, a );
650    p[4].set( a, d, c );
651    p[5].set( e, f, g );
652}
653
654typedef Frustum<float>  Frustumf;
655typedef Frustum<double> Frustumd;
656
657
658} // namespace Imath
659
660#endif
Note: See TracBrowser for help on using the repository browser.