1 ///////////////////////////////////////////////////////////////////////////
3 // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
6 // All rights reserved.
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
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
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.
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.
33 ///////////////////////////////////////////////////////////////////////////
37 #ifndef INCLUDED_IMATHFRUSTUM_H
38 #define INCLUDED_IMATHFRUSTUM_H
42 #include "ImathPlane.h"
43 #include "ImathLine.h"
44 #include "ImathMatrix.h"
45 #include "ImathLimits.h"
47 #include "IexMathExc.h"
49 #if defined _WIN32 || defined _WIN64
61 // template class Frustum<T>
63 // The frustum is always located with the eye point at the
64 // origin facing down -Z. This makes the Frustum class
65 // compatable with OpenGL (or anything that assumes a camera
66 // looks down -Z, hence with a right-handed coordinate system)
67 // but not with RenderMan which assumes the camera looks down
68 // +Z. Additional functions are provided for conversion from
69 // and from various camera coordinate spaces.
78 Frustum(const Frustum &);
79 Frustum(T near, T far, T left, T right, T top, T bottom, bool ortho=false);
80 Frustum(T near, T far, T fovx, T fovy, T aspect);
83 //--------------------
84 // Assignment operator
85 //--------------------
87 const Frustum &operator = (const Frustum &);
89 //--------------------
91 //--------------------
93 bool operator == (const Frustum<T> &src) const;
94 bool operator != (const Frustum<T> &src) const;
96 //--------------------------------------------------------
97 // Set functions change the entire state of the Frustum
98 //--------------------------------------------------------
100 void set(T near, T far,
105 void set(T near, T far, T fovx, T fovy, T aspect);
107 //------------------------------------------------------
108 // These functions modify an already valid frustum state
109 //------------------------------------------------------
111 void modifyNearAndFar(T near, T far);
112 void setOrthographic(bool);
118 bool orthographic() const { return _orthographic; }
119 T near() const { return _near; }
120 T far() const { return _far; }
121 T left() const { return _left; }
122 T right() const { return _right; }
123 T bottom() const { return _bottom; }
124 T top() const { return _top; }
126 //-----------------------------------------------------------------------
127 // Sets the planes in p to be the six bounding planes of the frustum, in
128 // the following order: top, right, bottom, left, near, far.
129 // Note that the planes have normals that point out of the frustum.
130 // The version of this routine that takes a matrix applies that matrix
131 // to transform the frustum before setting the planes.
132 //-----------------------------------------------------------------------
134 void planes(Plane3<T> p[6]);
135 void planes(Plane3<T> p[6], const Matrix44<T> &M);
137 //----------------------
138 // Derived Quantities
139 //----------------------
144 Matrix44<T> projectionMatrix() const;
146 //-----------------------------------------------------------------------
147 // Takes a rectangle in the screen space (i.e., -1 <= left <= right <= 1
148 // and -1 <= bottom <= top <= 1) of this Frustum, and returns a new
149 // Frustum whose near clipping-plane window is that rectangle in local
151 //-----------------------------------------------------------------------
153 Frustum<T> window(T left, T right, T top, T bottom) const;
155 //----------------------------------------------------------
156 // Projection is in screen space / Conversion from Z-Buffer
157 //----------------------------------------------------------
159 Line3<T> projectScreenToRay( const Vec2<T> & ) const;
160 Vec2<T> projectPointToScreen( const Vec3<T> & ) const;
162 T ZToDepth(long zval, long min, long max) const;
163 T normalizedZToDepth(T zval) const;
164 long DepthToZ(T depth, long zmin, long zmax) const;
166 T worldRadius(const Vec3<T> &p, T radius) const;
167 T screenRadius(const Vec3<T> &p, T radius) const;
172 Vec2<T> screenToLocal( const Vec2<T> & ) const;
173 Vec2<T> localToScreen( const Vec2<T> & ) const;
187 inline Frustum<T>::Frustum()
199 inline Frustum<T>::Frustum(const Frustum &f)
205 inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)
211 inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect)
213 set(near,far,fovx,fovy,aspect);
217 Frustum<T>::~Frustum()
223 Frustum<T>::operator = (const Frustum &f)
231 _orthographic = f._orthographic;
238 Frustum<T>::operator == (const Frustum<T> &src) const
241 _near == src._near &&
243 _left == src._left &&
244 _right == src._right &&
246 _bottom == src._bottom &&
247 _orthographic == src._orthographic;
252 Frustum<T>::operator != (const Frustum<T> &src) const
254 return !operator== (src);
258 void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)
270 void Frustum<T>::modifyNearAndFar(T n, T f)
278 Line3<T> lowerLeft( Vec3<T>(0,0,0), Vec3<T>(_left,_bottom,-_near) );
279 Line3<T> upperRight( Vec3<T>(0,0,0), Vec3<T>(_right,_top,-_near) );
280 Plane3<T> nearPlane( Vec3<T>(0,0,-1), n );
283 nearPlane.intersect(lowerLeft,ll);
284 nearPlane.intersect(upperRight,ur);
298 void Frustum<T>::setOrthographic(bool ortho)
300 _orthographic = ortho;
304 void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect)
306 if (fovx != 0 && fovy != 0)
307 throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");
311 _right = near * Math<T>::tan(fovx/2.0);
313 _top = ((_right - _left)/aspect)/2.0;
318 _top = near * Math<T>::tan(fovy/2.0);
320 _right = (_top - _bottom) * aspect / 2.0;
325 _orthographic = false;
329 T Frustum<T>::fovx() const
331 return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near);
335 T Frustum<T>::fovy() const
337 return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near);
341 T Frustum<T>::aspect() const
343 T rightMinusLeft = _right-_left;
344 T topMinusBottom = _top-_bottom;
346 if (abs(topMinusBottom) < 1 &&
347 abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))
349 throw Iex::DivzeroExc ("Bad viewing frustum: "
350 "aspect ratio cannot be computed.");
353 return rightMinusLeft / topMinusBottom;
357 Matrix44<T> Frustum<T>::projectionMatrix() const
359 T rightPlusLeft = _right+_left;
360 T rightMinusLeft = _right-_left;
362 T topPlusBottom = _top+_bottom;
363 T topMinusBottom = _top-_bottom;
365 T farPlusNear = _far+_near;
366 T farMinusNear = _far-_near;
368 if ((abs(rightMinusLeft) < 1 &&
369 abs(rightPlusLeft) > limits<T>::max() * abs(rightMinusLeft)) ||
370 (abs(topMinusBottom) < 1 &&
371 abs(topPlusBottom) > limits<T>::max() * abs(topMinusBottom)) ||
372 (abs(farMinusNear) < 1 &&
373 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)))
375 throw Iex::DivzeroExc ("Bad viewing frustum: "
376 "projection matrix cannot be computed.");
381 T tx = -rightPlusLeft / rightMinusLeft;
382 T ty = -topPlusBottom / topMinusBottom;
383 T tz = -farPlusNear / farMinusNear;
385 if ((abs(rightMinusLeft) < 1 &&
386 2 > limits<T>::max() * abs(rightMinusLeft)) ||
387 (abs(topMinusBottom) < 1 &&
388 2 > limits<T>::max() * abs(topMinusBottom)) ||
389 (abs(farMinusNear) < 1 &&
390 2 > limits<T>::max() * abs(farMinusNear)))
392 throw Iex::DivzeroExc ("Bad viewing frustum: "
393 "projection matrix cannot be computed.");
396 T A = 2 / rightMinusLeft;
397 T B = 2 / topMinusBottom;
398 T C = -2 / farMinusNear;
400 return Matrix44<T>( A, 0, 0, 0,
407 T A = rightPlusLeft / rightMinusLeft;
408 T B = topPlusBottom / topMinusBottom;
409 T C = -farPlusNear / farMinusNear;
411 T farTimesNear = -2 * _far * _near;
412 if (abs(farMinusNear) < 1 &&
413 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
415 throw Iex::DivzeroExc ("Bad viewing frustum: "
416 "projection matrix cannot be computed.");
419 T D = farTimesNear / farMinusNear;
421 T twoTimesNear = 2 * _near;
423 if ((abs(rightMinusLeft) < 1 &&
424 abs(twoTimesNear) > limits<T>::max() * abs(rightMinusLeft)) ||
425 (abs(topMinusBottom) < 1 &&
426 abs(twoTimesNear) > limits<T>::max() * abs(topMinusBottom)))
428 throw Iex::DivzeroExc ("Bad viewing frustum: "
429 "projection matrix cannot be computed.");
432 T E = twoTimesNear / rightMinusLeft;
433 T F = twoTimesNear / topMinusBottom;
435 return Matrix44<T>( E, 0, 0, 0,
443 Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const
445 // move it to 0->1 space
447 Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );
448 Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );
450 return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic);
455 Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const
457 return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,
458 _bottom + (_top-_bottom) * (1.f+s.y) / 2.f );
462 Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const
464 T leftPlusRight = _left - 2 * p.x + _right;
465 T leftMinusRight = _left-_right;
466 T bottomPlusTop = _bottom - 2 * p.y + _top;
467 T bottomMinusTop = _bottom-_top;
469 if ((abs(leftMinusRight) < 1 &&
470 abs(leftPlusRight) > limits<T>::max() * abs(leftMinusRight)) ||
471 (abs(bottomMinusTop) < 1 &&
472 abs(bottomPlusTop) > limits<T>::max() * abs(bottomMinusTop)))
474 throw Iex::DivzeroExc
475 ("Bad viewing frustum: "
476 "local-to-screen transformation cannot be computed");
479 return Vec2<T>( leftPlusRight / leftMinusRight,
480 bottomPlusTop / bottomMinusTop );
484 Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const
486 Vec2<T> point = screenToLocal(p);
488 return Line3<T>( Vec3<T>(point.x,point.y, 0.0),
489 Vec3<T>(point.x,point.y,-_near));
491 return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near));
495 Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const
497 if (orthographic() || point.z == 0)
498 return localToScreen( Vec2<T>( point.x, point.y ) );
500 return localToScreen( Vec2<T>( point.x * _near / -point.z,
501 point.y * _near / -point.z ) );
505 T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const
507 int zdiff = zmax - zmin;
511 throw Iex::DivzeroExc
512 ("Bad call to Frustum::ZToDepth: zmax == zmin");
515 if ( zval > zmax+1 ) zval -= zdiff;
517 T fzval = (T(zval) - T(zmin)) / T(zdiff);
518 return normalizedZToDepth(fzval);
522 T Frustum<T>::normalizedZToDepth(T zval) const
524 T Zp = zval * 2.0 - 1;
528 return -(Zp*(_far-_near) + (_far+_near))/2;
532 T farTimesNear = 2 * _far * _near;
533 T farMinusNear = Zp * (_far - _near) - _far - _near;
535 if (abs(farMinusNear) < 1 &&
536 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
538 throw Iex::DivzeroExc
539 ("Frustum::normalizedZToDepth cannot be computed. The "
540 "near and far clipping planes of the viewing frustum "
541 "may be too close to each other");
544 return farTimesNear / farMinusNear;
549 long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const
551 long zdiff = zmax - zmin;
552 T farMinusNear = _far-_near;
556 T farPlusNear = 2*depth + _far + _near;
558 if (abs(farMinusNear) < 1 &&
559 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
561 throw Iex::DivzeroExc
562 ("Bad viewing frustum: near and far clipping planes "
563 "are too close to each other");
566 T Zp = -farPlusNear/farMinusNear;
567 return long(0.5*(Zp+1)*zdiff) + zmin;
573 T farTimesNear = 2*_far*_near;
574 if (abs(depth) < 1 &&
575 abs(farTimesNear) > limits<T>::max() * abs(depth))
577 throw Iex::DivzeroExc
578 ("Bad call to DepthToZ function: value of `depth' "
582 T farPlusNear = farTimesNear/depth + _far + _near;
583 if (abs(farMinusNear) < 1 &&
584 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
586 throw Iex::DivzeroExc
587 ("Bad viewing frustum: near and far clipping planes "
588 "are too close to each other");
591 T Zp = farPlusNear/farMinusNear;
592 return long(0.5*(Zp+1)*zdiff) + zmin;
597 T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const
600 // Consider X-Z plane.
601 // X coord of projection of p = xp = p.x * (-_near / p.z)
602 // Let q be p + (radius, 0, 0).
603 // X coord of projection of q = xq = (p.x - radius) * (-_near / p.z)
604 // X coord of projection of segment from p to q = r = xp - xq
605 // = radius * (-_near / p.z)
606 // A similar analysis holds in the Y-Z plane.
607 // So r is the quantity we want to return.
609 if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z))
611 return radius * (-_near / p.z);
615 throw Iex::DivzeroExc
616 ("Bad call to Frustum::screenRadius: the magnitude of `p' "
620 return radius * (-_near / p.z);
624 T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const
626 if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near))
628 return radius * (p.z / -_near);
632 throw Iex::DivzeroExc
633 ("Bad viewing frustum: the near clipping plane is too "
639 void Frustum<T>::planes(Plane3<T> p[6])
642 // Plane order: Top, Right, Bottom, Left, Near, Far.
643 // Normals point outwards.
646 Vec3<T> a( _left, _bottom, -_near);
647 Vec3<T> b( _left, _top, -_near);
648 Vec3<T> c( _right, _top, -_near);
649 Vec3<T> d( _right, _bottom, -_near);
656 p[4].set( Vec3<T>(0, 0, 1), -_near );
657 p[5].set( Vec3<T>(0, 0,-1), _far );
662 void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)
665 // Plane order: Top, Right, Bottom, Left, Near, Far.
666 // Normals point outwards.
669 Vec3<T> a = Vec3<T>( _left, _bottom, -_near) * M;
670 Vec3<T> b = Vec3<T>( _left, _top, -_near) * M;
671 Vec3<T> c = Vec3<T>( _right, _top, -_near) * M;
672 Vec3<T> d = Vec3<T>( _right, _bottom, -_near) * M;
673 double s = _far / double(_near);
674 T farLeft = (T) (s * _left);
675 T farRight = (T) (s * _right);
676 T farTop = (T) (s * _top);
677 T farBottom = (T) (s * _bottom);
678 Vec3<T> e = Vec3<T>( farLeft, farBottom, -_far) * M;
679 Vec3<T> f = Vec3<T>( farLeft, farTop, -_far) * M;
680 Vec3<T> g = Vec3<T>( farRight, farTop, -_far) * M;
681 Vec3<T> o = Vec3<T>(0,0,0) * M;
691 typedef Frustum<float> Frustumf;
692 typedef Frustum<double> Frustumd;