00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #ifndef INCLUDED_IMATHFRUSTUM_H
00038 #define INCLUDED_IMATHFRUSTUM_H
00039
00040
00041 #include "ImathVec.h"
00042 #include "ImathPlane.h"
00043 #include "ImathLine.h"
00044 #include "ImathMatrix.h"
00045 #include "ImathLimits.h"
00046 #include "ImathFun.h"
00047 #include "IexMathExc.h"
00048
00049 #if defined _WIN32 || defined _WIN64
00050 #ifdef near
00051 #define _redef_near
00052 #undef near
00053 #endif
00054 #ifdef far
00055 #define _redef_far
00056 #undef far
00057 #endif
00058 #endif
00059
00060 namespace Imath {
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 template<class T>
00076 class Frustum
00077 {
00078 public:
00079 Frustum();
00080 Frustum(const Frustum &);
00081 Frustum(T near, T far, T left, T right, T top, T bottom, bool ortho=false);
00082 Frustum(T near, T far, T fovx, T fovy, T aspect);
00083 virtual ~Frustum();
00084
00085
00086
00087
00088
00089 const Frustum &operator = (const Frustum &);
00090
00091
00092
00093
00094
00095 bool operator == (const Frustum<T> &src) const;
00096 bool operator != (const Frustum<T> &src) const;
00097
00098
00099
00100
00101
00102 void set(T near, T far,
00103 T left, T right,
00104 T top, T bottom,
00105 bool ortho=false);
00106
00107 void set(T near, T far, T fovx, T fovy, T aspect);
00108
00109
00110
00111
00112
00113 void modifyNearAndFar(T near, T far);
00114 void setOrthographic(bool);
00115
00116
00117
00118
00119
00120 bool orthographic() const { return _orthographic; }
00121 T near() const { return _near; }
00122 T hither() const { return _near; }
00123 T far() const { return _far; }
00124 T yon() const { return _far; }
00125 T left() const { return _left; }
00126 T right() const { return _right; }
00127 T bottom() const { return _bottom; }
00128 T top() const { return _top; }
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138 void planes(Plane3<T> p[6]);
00139 void planes(Plane3<T> p[6], const Matrix44<T> &M);
00140
00141
00142
00143
00144
00145 T fovx() const;
00146 T fovy() const;
00147 T aspect() const;
00148 Matrix44<T> projectionMatrix() const;
00149
00150
00151
00152
00153
00154
00155
00156
00157 Frustum<T> window(T left, T right, T top, T bottom) const;
00158
00159
00160
00161
00162
00163 Line3<T> projectScreenToRay( const Vec2<T> & ) const;
00164 Vec2<T> projectPointToScreen( const Vec3<T> & ) const;
00165
00166 T ZToDepth(long zval, long min, long max) const;
00167 T normalizedZToDepth(T zval) const;
00168 long DepthToZ(T depth, long zmin, long zmax) const;
00169
00170 T worldRadius(const Vec3<T> &p, T radius) const;
00171 T screenRadius(const Vec3<T> &p, T radius) const;
00172
00173
00174 protected:
00175
00176 Vec2<T> screenToLocal( const Vec2<T> & ) const;
00177 Vec2<T> localToScreen( const Vec2<T> & ) const;
00178
00179 protected:
00180 T _near;
00181 T _far;
00182 T _left;
00183 T _right;
00184 T _top;
00185 T _bottom;
00186 bool _orthographic;
00187 };
00188
00189
00190 template<class T>
00191 inline Frustum<T>::Frustum()
00192 {
00193 set(T (0.1),
00194 T (1000.0),
00195 T (-1.0),
00196 T (1.0),
00197 T (1.0),
00198 T (-1.0),
00199 false);
00200 }
00201
00202 template<class T>
00203 inline Frustum<T>::Frustum(const Frustum &f)
00204 {
00205 *this = f;
00206 }
00207
00208 template<class T>
00209 inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)
00210 {
00211 set(n,f,l,r,t,b,o);
00212 }
00213
00214 template<class T>
00215 inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect)
00216 {
00217 set(near,far,fovx,fovy,aspect);
00218 }
00219
00220 template<class T>
00221 Frustum<T>::~Frustum()
00222 {
00223 }
00224
00225 template<class T>
00226 const Frustum<T> &
00227 Frustum<T>::operator = (const Frustum &f)
00228 {
00229 _near = f._near;
00230 _far = f._far;
00231 _left = f._left;
00232 _right = f._right;
00233 _top = f._top;
00234 _bottom = f._bottom;
00235 _orthographic = f._orthographic;
00236
00237 return *this;
00238 }
00239
00240 template <class T>
00241 bool
00242 Frustum<T>::operator == (const Frustum<T> &src) const
00243 {
00244 return
00245 _near == src._near &&
00246 _far == src._far &&
00247 _left == src._left &&
00248 _right == src._right &&
00249 _top == src._top &&
00250 _bottom == src._bottom &&
00251 _orthographic == src._orthographic;
00252 }
00253
00254 template <class T>
00255 inline bool
00256 Frustum<T>::operator != (const Frustum<T> &src) const
00257 {
00258 return !operator== (src);
00259 }
00260
00261 template<class T>
00262 void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)
00263 {
00264 _near = n;
00265 _far = f;
00266 _left = l;
00267 _right = r;
00268 _bottom = b;
00269 _top = t;
00270 _orthographic = o;
00271 }
00272
00273 template<class T>
00274 void Frustum<T>::modifyNearAndFar(T n, T f)
00275 {
00276 if ( _orthographic )
00277 {
00278 _near = n;
00279 }
00280 else
00281 {
00282 Line3<T> lowerLeft( Vec3<T>(0,0,0), Vec3<T>(_left,_bottom,-_near) );
00283 Line3<T> upperRight( Vec3<T>(0,0,0), Vec3<T>(_right,_top,-_near) );
00284 Plane3<T> nearPlane( Vec3<T>(0,0,-1), n );
00285
00286 Vec3<T> ll,ur;
00287 nearPlane.intersect(lowerLeft,ll);
00288 nearPlane.intersect(upperRight,ur);
00289
00290 _left = ll.x;
00291 _right = ur.x;
00292 _top = ur.y;
00293 _bottom = ll.y;
00294 _near = n;
00295 _far = f;
00296 }
00297
00298 _far = f;
00299 }
00300
00301 template<class T>
00302 void Frustum<T>::setOrthographic(bool ortho)
00303 {
00304 _orthographic = ortho;
00305 }
00306
00307 template<class T>
00308 void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect)
00309 {
00310 if (fovx != 0 && fovy != 0)
00311 throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");
00312
00313 if (fovx != 0)
00314 {
00315 _right = near * Math<T>::tan (fovx / 2);
00316 _left = -_right;
00317 _top = ((_right - _left) / aspect) / 2;
00318 _bottom = -_top;
00319 }
00320 else
00321 {
00322 _top = near * Math<T>::tan (fovy / 2);
00323 _bottom = -_top;
00324 _right = (_top - _bottom) * aspect / 2;
00325 _left = -_right;
00326 }
00327 _near = near;
00328 _far = far;
00329 _orthographic = false;
00330 }
00331
00332 template<class T>
00333 T Frustum<T>::fovx() const
00334 {
00335 return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near);
00336 }
00337
00338 template<class T>
00339 T Frustum<T>::fovy() const
00340 {
00341 return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near);
00342 }
00343
00344 template<class T>
00345 T Frustum<T>::aspect() const
00346 {
00347 T rightMinusLeft = _right-_left;
00348 T topMinusBottom = _top-_bottom;
00349
00350 if (abs(topMinusBottom) < 1 &&
00351 abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))
00352 {
00353 throw Iex::DivzeroExc ("Bad viewing frustum: "
00354 "aspect ratio cannot be computed.");
00355 }
00356
00357 return rightMinusLeft / topMinusBottom;
00358 }
00359
00360 template<class T>
00361 Matrix44<T> Frustum<T>::projectionMatrix() const
00362 {
00363 T rightPlusLeft = _right+_left;
00364 T rightMinusLeft = _right-_left;
00365
00366 T topPlusBottom = _top+_bottom;
00367 T topMinusBottom = _top-_bottom;
00368
00369 T farPlusNear = _far+_near;
00370 T farMinusNear = _far-_near;
00371
00372 if ((abs(rightMinusLeft) < 1 &&
00373 abs(rightPlusLeft) > limits<T>::max() * abs(rightMinusLeft)) ||
00374 (abs(topMinusBottom) < 1 &&
00375 abs(topPlusBottom) > limits<T>::max() * abs(topMinusBottom)) ||
00376 (abs(farMinusNear) < 1 &&
00377 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)))
00378 {
00379 throw Iex::DivzeroExc ("Bad viewing frustum: "
00380 "projection matrix cannot be computed.");
00381 }
00382
00383 if ( _orthographic )
00384 {
00385 T tx = -rightPlusLeft / rightMinusLeft;
00386 T ty = -topPlusBottom / topMinusBottom;
00387 T tz = -farPlusNear / farMinusNear;
00388
00389 if ((abs(rightMinusLeft) < 1 &&
00390 2 > limits<T>::max() * abs(rightMinusLeft)) ||
00391 (abs(topMinusBottom) < 1 &&
00392 2 > limits<T>::max() * abs(topMinusBottom)) ||
00393 (abs(farMinusNear) < 1 &&
00394 2 > limits<T>::max() * abs(farMinusNear)))
00395 {
00396 throw Iex::DivzeroExc ("Bad viewing frustum: "
00397 "projection matrix cannot be computed.");
00398 }
00399
00400 T A = 2 / rightMinusLeft;
00401 T B = 2 / topMinusBottom;
00402 T C = -2 / farMinusNear;
00403
00404 return Matrix44<T>( A, 0, 0, 0,
00405 0, B, 0, 0,
00406 0, 0, C, 0,
00407 tx, ty, tz, 1.f );
00408 }
00409 else
00410 {
00411 T A = rightPlusLeft / rightMinusLeft;
00412 T B = topPlusBottom / topMinusBottom;
00413 T C = -farPlusNear / farMinusNear;
00414
00415 T farTimesNear = -2 * _far * _near;
00416 if (abs(farMinusNear) < 1 &&
00417 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
00418 {
00419 throw Iex::DivzeroExc ("Bad viewing frustum: "
00420 "projection matrix cannot be computed.");
00421 }
00422
00423 T D = farTimesNear / farMinusNear;
00424
00425 T twoTimesNear = 2 * _near;
00426
00427 if ((abs(rightMinusLeft) < 1 &&
00428 abs(twoTimesNear) > limits<T>::max() * abs(rightMinusLeft)) ||
00429 (abs(topMinusBottom) < 1 &&
00430 abs(twoTimesNear) > limits<T>::max() * abs(topMinusBottom)))
00431 {
00432 throw Iex::DivzeroExc ("Bad viewing frustum: "
00433 "projection matrix cannot be computed.");
00434 }
00435
00436 T E = twoTimesNear / rightMinusLeft;
00437 T F = twoTimesNear / topMinusBottom;
00438
00439 return Matrix44<T>( E, 0, 0, 0,
00440 0, F, 0, 0,
00441 A, B, C, -1,
00442 0, 0, D, 0 );
00443 }
00444 }
00445
00446 template<class T>
00447 Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const
00448 {
00449
00450
00451 Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );
00452 Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );
00453
00454 return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic);
00455 }
00456
00457
00458 template<class T>
00459 Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const
00460 {
00461 return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,
00462 _bottom + (_top-_bottom) * (1.f+s.y) / 2.f );
00463 }
00464
00465 template<class T>
00466 Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const
00467 {
00468 T leftPlusRight = _left - 2 * p.x + _right;
00469 T leftMinusRight = _left-_right;
00470 T bottomPlusTop = _bottom - 2 * p.y + _top;
00471 T bottomMinusTop = _bottom-_top;
00472
00473 if ((abs(leftMinusRight) < 1 &&
00474 abs(leftPlusRight) > limits<T>::max() * abs(leftMinusRight)) ||
00475 (abs(bottomMinusTop) < 1 &&
00476 abs(bottomPlusTop) > limits<T>::max() * abs(bottomMinusTop)))
00477 {
00478 throw Iex::DivzeroExc
00479 ("Bad viewing frustum: "
00480 "local-to-screen transformation cannot be computed");
00481 }
00482
00483 return Vec2<T>( leftPlusRight / leftMinusRight,
00484 bottomPlusTop / bottomMinusTop );
00485 }
00486
00487 template<class T>
00488 Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const
00489 {
00490 Vec2<T> point = screenToLocal(p);
00491 if (orthographic())
00492 return Line3<T>( Vec3<T>(point.x,point.y, 0.0),
00493 Vec3<T>(point.x,point.y,-_near));
00494 else
00495 return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near));
00496 }
00497
00498 template<class T>
00499 Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const
00500 {
00501 if (orthographic() || point.z == 0)
00502 return localToScreen( Vec2<T>( point.x, point.y ) );
00503 else
00504 return localToScreen( Vec2<T>( point.x * _near / -point.z,
00505 point.y * _near / -point.z ) );
00506 }
00507
00508 template<class T>
00509 T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const
00510 {
00511 int zdiff = zmax - zmin;
00512
00513 if (zdiff == 0)
00514 {
00515 throw Iex::DivzeroExc
00516 ("Bad call to Frustum::ZToDepth: zmax == zmin");
00517 }
00518
00519 if ( zval > zmax+1 ) zval -= zdiff;
00520
00521 T fzval = (T(zval) - T(zmin)) / T(zdiff);
00522 return normalizedZToDepth(fzval);
00523 }
00524
00525 template<class T>
00526 T Frustum<T>::normalizedZToDepth(T zval) const
00527 {
00528 T Zp = zval * 2.0 - 1;
00529
00530 if ( _orthographic )
00531 {
00532 return -(Zp*(_far-_near) + (_far+_near))/2;
00533 }
00534 else
00535 {
00536 T farTimesNear = 2 * _far * _near;
00537 T farMinusNear = Zp * (_far - _near) - _far - _near;
00538
00539 if (abs(farMinusNear) < 1 &&
00540 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))
00541 {
00542 throw Iex::DivzeroExc
00543 ("Frustum::normalizedZToDepth cannot be computed. The "
00544 "near and far clipping planes of the viewing frustum "
00545 "may be too close to each other");
00546 }
00547
00548 return farTimesNear / farMinusNear;
00549 }
00550 }
00551
00552 template<class T>
00553 long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const
00554 {
00555 long zdiff = zmax - zmin;
00556 T farMinusNear = _far-_near;
00557
00558 if ( _orthographic )
00559 {
00560 T farPlusNear = 2*depth + _far + _near;
00561
00562 if (abs(farMinusNear) < 1 &&
00563 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
00564 {
00565 throw Iex::DivzeroExc
00566 ("Bad viewing frustum: near and far clipping planes "
00567 "are too close to each other");
00568 }
00569
00570 T Zp = -farPlusNear/farMinusNear;
00571 return long(0.5*(Zp+1)*zdiff) + zmin;
00572 }
00573 else
00574 {
00575
00576
00577 T farTimesNear = 2*_far*_near;
00578 if (abs(depth) < 1 &&
00579 abs(farTimesNear) > limits<T>::max() * abs(depth))
00580 {
00581 throw Iex::DivzeroExc
00582 ("Bad call to DepthToZ function: value of `depth' "
00583 "is too small");
00584 }
00585
00586 T farPlusNear = farTimesNear/depth + _far + _near;
00587 if (abs(farMinusNear) < 1 &&
00588 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))
00589 {
00590 throw Iex::DivzeroExc
00591 ("Bad viewing frustum: near and far clipping planes "
00592 "are too close to each other");
00593 }
00594
00595 T Zp = farPlusNear/farMinusNear;
00596 return long(0.5*(Zp+1)*zdiff) + zmin;
00597 }
00598 }
00599
00600 template<class T>
00601 T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const
00602 {
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613 if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z))
00614 {
00615 return radius * (-_near / p.z);
00616 }
00617 else
00618 {
00619 throw Iex::DivzeroExc
00620 ("Bad call to Frustum::screenRadius: the magnitude of `p' "
00621 "is too small");
00622 }
00623
00624 return radius * (-_near / p.z);
00625 }
00626
00627 template<class T>
00628 T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const
00629 {
00630 if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near))
00631 {
00632 return radius * (p.z / -_near);
00633 }
00634 else
00635 {
00636 throw Iex::DivzeroExc
00637 ("Bad viewing frustum: the near clipping plane is too "
00638 "close to zero");
00639 }
00640 }
00641
00642 template<class T>
00643 void Frustum<T>::planes(Plane3<T> p[6])
00644 {
00645
00646
00647
00648
00649
00650 if (! _orthographic)
00651 {
00652 Vec3<T> a( _left, _bottom, -_near);
00653 Vec3<T> b( _left, _top, -_near);
00654 Vec3<T> c( _right, _top, -_near);
00655 Vec3<T> d( _right, _bottom, -_near);
00656 Vec3<T> o(0,0,0);
00657
00658 p[0].set( o, c, b );
00659 p[1].set( o, d, c );
00660 p[2].set( o, a, d );
00661 p[3].set( o, b, a );
00662 }
00663 else
00664 {
00665 p[0].set( Vec3<T>( 0, 1, 0), _top );
00666 p[1].set( Vec3<T>( 1, 0, 0), _right );
00667 p[2].set( Vec3<T>( 0,-1, 0),-_bottom );
00668 p[3].set( Vec3<T>(-1, 0, 0),-_left );
00669 }
00670 p[4].set( Vec3<T>(0, 0, 1), -_near );
00671 p[5].set( Vec3<T>(0, 0,-1), _far );
00672 }
00673
00674
00675 template<class T>
00676 void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)
00677 {
00678
00679
00680
00681
00682
00683 Vec3<T> a = Vec3<T>( _left, _bottom, -_near) * M;
00684 Vec3<T> b = Vec3<T>( _left, _top, -_near) * M;
00685 Vec3<T> c = Vec3<T>( _right, _top, -_near) * M;
00686 Vec3<T> d = Vec3<T>( _right, _bottom, -_near) * M;
00687 if (! _orthographic)
00688 {
00689 double s = _far / double(_near);
00690 T farLeft = (T) (s * _left);
00691 T farRight = (T) (s * _right);
00692 T farTop = (T) (s * _top);
00693 T farBottom = (T) (s * _bottom);
00694 Vec3<T> e = Vec3<T>( farLeft, farBottom, -_far) * M;
00695 Vec3<T> f = Vec3<T>( farLeft, farTop, -_far) * M;
00696 Vec3<T> g = Vec3<T>( farRight, farTop, -_far) * M;
00697 Vec3<T> o = Vec3<T>(0,0,0) * M;
00698 p[0].set( o, c, b );
00699 p[1].set( o, d, c );
00700 p[2].set( o, a, d );
00701 p[3].set( o, b, a );
00702 p[4].set( a, d, c );
00703 p[5].set( e, f, g );
00704 }
00705 else
00706 {
00707 Vec3<T> e = Vec3<T>( _left, _bottom, -_far) * M;
00708 Vec3<T> f = Vec3<T>( _left, _top, -_far) * M;
00709 Vec3<T> g = Vec3<T>( _right, _top, -_far) * M;
00710 Vec3<T> h = Vec3<T>( _right, _bottom, -_far) * M;
00711 p[0].set( c, g, f );
00712 p[1].set( d, h, g );
00713 p[2].set( a, e, h );
00714 p[3].set( b, f, e );
00715 p[4].set( a, d, c );
00716 p[5].set( e, f, g );
00717 }
00718 }
00719
00720 typedef Frustum<float> Frustumf;
00721 typedef Frustum<double> Frustumd;
00722
00723
00724 }
00725
00726
00727 #if defined _WIN32 || defined _WIN64
00728 #ifdef _redef_near
00729 #define near
00730 #endif
00731 #ifdef _redef_far
00732 #define far
00733 #endif
00734 #endif
00735
00736 #endif