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>
52
// template class Frustum<T>
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.
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);
74
//--------------------
75
// Assignment operator
76
//--------------------
78
const Frustum &operator = (const Frustum &);
80
//--------------------------------------------------------
81
// Set functions change the entire state of the Frustum
82
//--------------------------------------------------------
84
void set(T near, T far,
89
void set(T near, T far, T fovx, T fovy, T aspect);
91
//------------------------------------------------------
92
// These functions modify an already valid frustum state
93
//------------------------------------------------------
95
void modifyNearAndFar(T near, T far);
96
void setOrthographic(bool);
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; }
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
//-----------------------------------------------------------------------
118
void planes(Plane3<T> p[6]);
119
void planes(Plane3<T> p[6], const Matrix44<T> &M);
121
//----------------------
122
// Derived Quantities
123
//----------------------
128
Matrix44<T> projectionMatrix() const;
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
135
//-----------------------------------------------------------------------
137
Frustum<T> window(T left, T right, T top, T bottom) const;
139
//----------------------------------------------------------
140
// Projection is in screen space / Conversion from Z-Buffer
141
//----------------------------------------------------------
143
Line3<T> projectScreenToRay( const Vec2<T> & ) const;
144
Vec2<T> projectPointToScreen( const Vec3<T> & ) const;
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;
150
T worldRadius(const Vec3<T> &p, T radius) const;
151
T screenRadius(const Vec3<T> &p, T radius) const;
156
Vec2<T> screenToLocal( const Vec2<T> & ) const;
157
Vec2<T> localToScreen( const Vec2<T> & ) const;
171
inline Frustum<T>::Frustum()
183
inline Frustum<T>::Frustum(const Frustum &f)
189
inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)
195
inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect)
197
set(near,far,fovx,fovy,aspect);
201
Frustum<T>::~Frustum()
207
Frustum<T>::operator = (const Frustum &f)
215
_orthographic = f._orthographic;
221
void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)
233
void Frustum<T>::modifyNearAndFar(T n, T f)
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 );
246
nearPlane.intersect(lowerLeft,ll);
247
nearPlane.intersect(upperRight,ur);
261
void Frustum<T>::setOrthographic(bool ortho)
263
_orthographic = ortho;
267
void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect)
269
if (fovx != 0 && fovy != 0)
270
throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");
274
_right = near * Math<T>::tan(fovx/2.0);
276
_top = ((_right - _left)/aspect)/2.0;
281
_top = near * Math<T>::tan(fovy/2.0);
283
_right = (_top - _bottom) * aspect / 2.0;
288
_orthographic = false;
292
T Frustum<T>::fovx() const
294
return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near);
298
T Frustum<T>::fovy() const
300
return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near);
304
T Frustum<T>::aspect() const
306
T rightMinusLeft = _right-_left;
307
T topMinusBottom = _top-_bottom;
309
if (abs(topMinusBottom) < 1 &&
310
abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))
312
throw Iex::DivzeroExc ("Bad viewing frustum: "
313
"aspect ratio cannot be computed.");
316
return rightMinusLeft / topMinusBottom;
320
Matrix44<T> Frustum<T>::projectionMatrix() const
322
T rightPlusLeft = _right+_left;
323
T rightMinusLeft = _right-_left;
325
T topPlusBottom = _top+_bottom;
326
T topMinusBottom = _top-_bottom;
328
T farPlusNear = _far+_near;
329
T farMinusNear = _far-_near;
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)))
338
throw Iex::DivzeroExc ("Bad viewing frustum: "
339
"projection matrix cannot be computed.");
344
T tx = -rightPlusLeft / rightMinusLeft;
345
T ty = -topPlusBottom / topMinusBottom;
346
T tz = -farPlusNear / farMinusNear;
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)))
355
throw Iex::DivzeroExc ("Bad viewing frustum: "
356
"projection matrix cannot be computed.");
359
T A = 2 / rightMinusLeft;
360
T B = 2 / topMinusBottom;
361
T C = -2 / farMinusNear;
363
return Matrix44<T>( A, 0, 0, 0,
370
T A = rightPlusLeft / rightMinusLeft;
371
T B = topPlusBottom / topMinusBottom;
372
T C = -farPlusNear / farMinusNear;
374
T farTimesNear = -2 * _far * _near;
375
if (abs(farMinusNear) < 1 &&
376
abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
378
throw Iex::DivzeroExc ("Bad viewing frustum: "
379
"projection matrix cannot be computed.");
382
T D = farTimesNear / farMinusNear;
384
T twoTimesNear = 2 * _near;
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)))
391
throw Iex::DivzeroExc ("Bad viewing frustum: "
392
"projection matrix cannot be computed.");
395
T E = twoTimesNear / rightMinusLeft;
396
T F = twoTimesNear / topMinusBottom;
398
return Matrix44<T>( E, 0, 0, 0,
406
Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const
408
// move it to 0->1 space
410
Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );
411
Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );
413
return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic);
418
Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const
420
return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,
421
_bottom + (_top-_bottom) * (1.f+s.y) / 2.f );
425
Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const
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;
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)))
437
throw Iex::DivzeroExc
438
("Bad viewing frustum: "
439
"local-to-screen transformation cannot be computed");
442
return Vec2<T>( leftPlusRight / leftMinusRight,
443
bottomPlusTop / bottomMinusTop );
447
Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const
449
Vec2<T> point = screenToLocal(p);
451
return Line3<T>( Vec3<T>(point.x,point.y, 0.0),
452
Vec3<T>(point.x,point.y,-_near));
454
return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near));
458
Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const
460
if (orthographic() || point.z == 0)
461
return localToScreen( Vec2<T>( point.x, point.y ) );
463
return localToScreen( Vec2<T>( point.x * _near / -point.z,
464
point.y * _near / -point.z ) );
468
T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const
470
int zdiff = zmax - zmin;
474
throw Iex::DivzeroExc
475
("Bad call to Frustum::ZToDepth: zmax == zmin");
478
if ( zval > zmax+1 ) zval -= zdiff;
480
T fzval = (T(zval) - T(zmin)) / T(zdiff);
481
return normalizedZToDepth(fzval);
485
T Frustum<T>::normalizedZToDepth(T zval) const
487
T Zp = zval * 2.0 - 1;
491
return -(Zp*(_far-_near) + (_far+_near))/2;
495
T farTimesNear = 2 * _far * _near;
496
T farMinusNear = Zp * (_far - _near) - _far - _near;
498
if (abs(farMinusNear) < 1 &&
499
abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
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");
507
return farTimesNear / farMinusNear;
512
long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const
514
long zdiff = zmax - zmin;
515
T farMinusNear = _far-_near;
519
T farPlusNear = 2*depth + _far + _near;
521
if (abs(farMinusNear) < 1 &&
522
abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
524
throw Iex::DivzeroExc
525
("Bad viewing frustum: near and far clipping planes "
526
"are too close to each other");
529
T Zp = -farPlusNear/farMinusNear;
530
return long(0.5*(Zp+1)*zdiff) + zmin;
536
T farTimesNear = 2*_far*_near;
537
if (abs(depth) < 1 &&
538
abs(farTimesNear) > limits<T>::max() * abs(depth))
540
throw Iex::DivzeroExc
541
("Bad call to DepthToZ function: value of `depth' "
545
T farPlusNear = farTimesNear/depth + _far + _near;
546
if (abs(farMinusNear) < 1 &&
547
abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
549
throw Iex::DivzeroExc
550
("Bad viewing frustum: near and far clipping planes "
551
"are too close to each other");
554
T Zp = farPlusNear/farMinusNear;
555
return long(0.5*(Zp+1)*zdiff) + zmin;
560
T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const
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.
572
if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z))
574
return radius * (-_near / p.z);
578
throw Iex::DivzeroExc
579
("Bad call to Frustum::screenRadius: the magnitude of `p' "
583
return radius * (-_near / p.z);
587
T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const
589
if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near))
591
return radius * (p.z / -_near);
595
throw Iex::DivzeroExc
596
("Bad viewing frustum: the near clipping plane is too "
602
void Frustum<T>::planes(Plane3<T> p[6])
605
// Plane order: Top, Right, Bottom, Left, Near, Far.
606
// Normals point outwards.
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);
619
p[4].set( Vec3<T>(0, 0, 1), -_near );
620
p[5].set( Vec3<T>(0, 0,-1), _far );
625
void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)
628
// Plane order: Top, Right, Bottom, Left, Near, Far.
629
// Normals point outwards.
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;
654
typedef Frustum<float> Frustumf;
655
typedef Frustum<double> Frustumd;