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 49#if defined _WIN32 || defined _WIN64 50 #ifdef near 51 #undef near 52 #endif 53 #ifdef far 54 #undef far 55 #endif 56#endif 57 58namespace Imath { 59 60// 61// template class Frustum<T> 62// 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. 70// 71 72 73template<class T> 74class Frustum 75{ 76 public: 77 Frustum(); 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); 81 virtual ~Frustum(); 82 83 //-------------------- 84 // Assignment operator 85 //-------------------- 86 87 const Frustum &operator = (const Frustum &); 88 89 //-------------------- 90 // Operators: ==, != 91 //-------------------- 92 93 bool operator == (const Frustum<T> &src) const; 94 bool operator != (const Frustum<T> &src) const; 95 96 //-------------------------------------------------------- 97 // Set functions change the entire state of the Frustum 98 //-------------------------------------------------------- 99 100 void set(T near, T far, 101 T left, T right, 102 T top, T bottom, 103 bool ortho=false); 104 105 void set(T near, T far, T fovx, T fovy, T aspect); 106 107 //------------------------------------------------------ 108 // These functions modify an already valid frustum state 109 //------------------------------------------------------ 110 111 void modifyNearAndFar(T near, T far); 112 void setOrthographic(bool); 113 114 //-------------- 115 // Access 116 //-------------- 117 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; } 125 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 //----------------------------------------------------------------------- 133 134 void planes(Plane3<T> p[6]); 135 void planes(Plane3<T> p[6], const Matrix44<T> &M); 136 137 //---------------------- 138 // Derived Quantities 139 //---------------------- 140 141 T fovx() const; 142 T fovy() const; 143 T aspect() const; 144 Matrix44<T> projectionMatrix() const; 145 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 150 // space. 151 //----------------------------------------------------------------------- 152 153 Frustum<T> window(T left, T right, T top, T bottom) const; 154 155 //---------------------------------------------------------- 156 // Projection is in screen space / Conversion from Z-Buffer 157 //---------------------------------------------------------- 158 159 Line3<T> projectScreenToRay( const Vec2<T> & ) const; 160 Vec2<T> projectPointToScreen( const Vec3<T> & ) const; 161 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; 165 166 T worldRadius(const Vec3<T> &p, T radius) const; 167 T screenRadius(const Vec3<T> &p, T radius) const; 168 169 170 protected: 171 172 Vec2<T> screenToLocal( const Vec2<T> & ) const; 173 Vec2<T> localToScreen( const Vec2<T> & ) const; 174 175 protected: 176 T _near; 177 T _far; 178 T _left; 179 T _right; 180 T _top; 181 T _bottom; 182 bool _orthographic; 183}; 184 185 186template<class T> 187inline Frustum<T>::Frustum() 188{ 189 set(T (0.1), 190 T (1000.0), 191 T (-1.0), 192 T (1.0), 193 T (1.0), 194 T (-1.0), 195 false); 196} 197 198template<class T> 199inline Frustum<T>::Frustum(const Frustum &f) 200{ 201 *this = f; 202} 203 204template<class T> 205inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o) 206{ 207 set(n,f,l,r,t,b,o); 208} 209 210template<class T> 211inline Frustum<T>::Frustum(T near, T far, T fovx, T fovy, T aspect) 212{ 213 set(near,far,fovx,fovy,aspect); 214} 215 216template<class T> 217Frustum<T>::~Frustum() 218{ 219} 220 221template<class T> 222const Frustum<T> & 223Frustum<T>::operator = (const Frustum &f) 224{ 225 _near = f._near; 226 _far = f._far; 227 _left = f._left; 228 _right = f._right; 229 _top = f._top; 230 _bottom = f._bottom; 231 _orthographic = f._orthographic; 232 233 return *this; 234} 235 236template <class T> 237bool 238Frustum<T>::operator == (const Frustum<T> &src) const 239{ 240 return 241 _near == src._near && 242 _far == src._far && 243 _left == src._left && 244 _right == src._right && 245 _top == src._top && 246 _bottom == src._bottom && 247 _orthographic == src._orthographic; 248} 249 250template <class T> 251inline bool 252Frustum<T>::operator != (const Frustum<T> &src) const 253{ 254 return !operator== (src); 255} 256 257template<class T> 258void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o) 259{ 260 _near = n; 261 _far = f; 262 _left = l; 263 _right = r; 264 _bottom = b; 265 _top = t; 266 _orthographic = o; 267} 268 269template<class T> 270void Frustum<T>::modifyNearAndFar(T n, T f) 271{ 272 if ( _orthographic ) 273 { 274 _near = n; 275 } 276 else 277 { 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 ); 281 282 Vec3<T> ll,ur; 283 nearPlane.intersect(lowerLeft,ll); 284 nearPlane.intersect(upperRight,ur); 285 286 _left = ll.x; 287 _right = ur.x; 288 _top = ur.y; 289 _bottom = ll.y; 290 _near = n; 291 _far = f; 292 } 293 294 _far = f; 295} 296 297template<class T> 298void Frustum<T>::setOrthographic(bool ortho) 299{ 300 _orthographic = ortho; 301} 302 303template<class T> 304void Frustum<T>::set(T near, T far, T fovx, T fovy, T aspect) 305{ 306 if (fovx != 0 && fovy != 0) 307 throw Iex::ArgExc ("fovx and fovy cannot both be non-zero."); 308 309 if (fovx != 0) 310 { 311 _right = near * Math<T>::tan(fovx/2.0); 312 _left = -_right; 313 _top = ((_right - _left)/aspect)/2.0; 314 _bottom = -_top; 315 } 316 else 317 { 318 _top = near * Math<T>::tan(fovy/2.0); 319 _bottom = -_top; 320 _right = (_top - _bottom) * aspect / 2.0; 321 _left = -_right; 322 } 323 _near = near; 324 _far = far; 325 _orthographic = false; 326} 327 328template<class T> 329T Frustum<T>::fovx() const 330{ 331 return Math<T>::atan2(_right,_near) - Math<T>::atan2(_left,_near); 332} 333 334template<class T> 335T Frustum<T>::fovy() const 336{ 337 return Math<T>::atan2(_top,_near) - Math<T>::atan2(_bottom,_near); 338} 339 340template<class T> 341T Frustum<T>::aspect() const 342{ 343 T rightMinusLeft = _right-_left; 344 T topMinusBottom = _top-_bottom; 345 346 if (abs(topMinusBottom) < 1 && 347 abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom)) 348 { 349 throw Iex::DivzeroExc ("Bad viewing frustum: " 350 "aspect ratio cannot be computed."); 351 } 352 353 return rightMinusLeft / topMinusBottom; 354} 355 356template<class T> 357Matrix44<T> Frustum<T>::projectionMatrix() const 358{ 359 T rightPlusLeft = _right+_left; 360 T rightMinusLeft = _right-_left; 361 362 T topPlusBottom = _top+_bottom; 363 T topMinusBottom = _top-_bottom; 364 365 T farPlusNear = _far+_near; 366 T farMinusNear = _far-_near; 367 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))) 374 { 375 throw Iex::DivzeroExc ("Bad viewing frustum: " 376 "projection matrix cannot be computed."); 377 } 378 379 if ( _orthographic ) 380 { 381 T tx = -rightPlusLeft / rightMinusLeft; 382 T ty = -topPlusBottom / topMinusBottom; 383 T tz = -farPlusNear / farMinusNear; 384 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))) 391 { 392 throw Iex::DivzeroExc ("Bad viewing frustum: " 393 "projection matrix cannot be computed."); 394 } 395 396 T A = 2 / rightMinusLeft; 397 T B = 2 / topMinusBottom; 398 T C = -2 / farMinusNear; 399 400 return Matrix44<T>( A, 0, 0, 0, 401 0, B, 0, 0, 402 0, 0, C, 0, 403 tx, ty, tz, 1.f ); 404 } 405 else 406 { 407 T A = rightPlusLeft / rightMinusLeft; 408 T B = topPlusBottom / topMinusBottom; 409 T C = -farPlusNear / farMinusNear; 410 411 T farTimesNear = -2 * _far * _near; 412 if (abs(farMinusNear) < 1 && 413 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear)) 414 { 415 throw Iex::DivzeroExc ("Bad viewing frustum: " 416 "projection matrix cannot be computed."); 417 } 418 419 T D = farTimesNear / farMinusNear; 420 421 T twoTimesNear = 2 * _near; 422 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))) 427 { 428 throw Iex::DivzeroExc ("Bad viewing frustum: " 429 "projection matrix cannot be computed."); 430 } 431 432 T E = twoTimesNear / rightMinusLeft; 433 T F = twoTimesNear / topMinusBottom; 434 435 return Matrix44<T>( E, 0, 0, 0, 436 0, F, 0, 0, 437 A, B, C, -1, 438 0, 0, D, 0 ); 439 } 440} 441 442template<class T> 443Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const 444{ 445 // move it to 0->1 space 446 447 Vec2<T> bl = screenToLocal( Vec2<T>(l,b) ); 448 Vec2<T> tr = screenToLocal( Vec2<T>(r,t) ); 449 450 return Frustum<T>(_near, _far, bl.x, tr.x, tr.y, bl.y, _orthographic); 451} 452 453 454template<class T> 455Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const 456{ 457 return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f, 458 _bottom + (_top-_bottom) * (1.f+s.y) / 2.f ); 459} 460 461template<class T> 462Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const 463{ 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; 468 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))) 473 { 474 throw Iex::DivzeroExc 475 ("Bad viewing frustum: " 476 "local-to-screen transformation cannot be computed"); 477 } 478 479 return Vec2<T>( leftPlusRight / leftMinusRight, 480 bottomPlusTop / bottomMinusTop ); 481} 482 483template<class T> 484Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const 485{ 486 Vec2<T> point = screenToLocal(p); 487 if (orthographic()) 488 return Line3<T>( Vec3<T>(point.x,point.y, 0.0), 489 Vec3<T>(point.x,point.y,-_near)); 490 else 491 return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_near)); 492} 493 494template<class T> 495Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const 496{ 497 if (orthographic() || point.z == 0) 498 return localToScreen( Vec2<T>( point.x, point.y ) ); 499 else 500 return localToScreen( Vec2<T>( point.x * _near / -point.z, 501 point.y * _near / -point.z ) ); 502} 503 504template<class T> 505T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const 506{ 507 int zdiff = zmax - zmin; 508 509 if (zdiff == 0) 510 { 511 throw Iex::DivzeroExc 512 ("Bad call to Frustum::ZToDepth: zmax == zmin"); 513 } 514 515 if ( zval > zmax+1 ) zval -= zdiff; 516 517 T fzval = (T(zval) - T(zmin)) / T(zdiff); 518 return normalizedZToDepth(fzval); 519} 520 521template<class T> 522T Frustum<T>::normalizedZToDepth(T zval) const 523{ 524 T Zp = zval * 2.0 - 1; 525 526 if ( _orthographic ) 527 { 528 return -(Zp*(_far-_near) + (_far+_near))/2; 529 } 530 else 531 { 532 T farTimesNear = 2 * _far * _near; 533 T farMinusNear = Zp * (_far - _near) - _far - _near; 534 535 if (abs(farMinusNear) < 1 && 536 abs(farTimesNear) > limits<T>::max() * abs(farMinusNear)) 537 { 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"); 542 } 543 544 return farTimesNear / farMinusNear; 545 } 546} 547 548template<class T> 549long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const 550{ 551 long zdiff = zmax - zmin; 552 T farMinusNear = _far-_near; 553 554 if ( _orthographic ) 555 { 556 T farPlusNear = 2*depth + _far + _near; 557 558 if (abs(farMinusNear) < 1 && 559 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)) 560 { 561 throw Iex::DivzeroExc 562 ("Bad viewing frustum: near and far clipping planes " 563 "are too close to each other"); 564 } 565 566 T Zp = -farPlusNear/farMinusNear; 567 return long(0.5*(Zp+1)*zdiff) + zmin; 568 } 569 else 570 { 571 // Perspective 572 573 T farTimesNear = 2*_far*_near; 574 if (abs(depth) < 1 && 575 abs(farTimesNear) > limits<T>::max() * abs(depth)) 576 { 577 throw Iex::DivzeroExc 578 ("Bad call to DepthToZ function: value of `depth' " 579 "is too small"); 580 } 581 582 T farPlusNear = farTimesNear/depth + _far + _near; 583 if (abs(farMinusNear) < 1 && 584 abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)) 585 { 586 throw Iex::DivzeroExc 587 ("Bad viewing frustum: near and far clipping planes " 588 "are too close to each other"); 589 } 590 591 T Zp = farPlusNear/farMinusNear; 592 return long(0.5*(Zp+1)*zdiff) + zmin; 593 } 594} 595 596template<class T> 597T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const 598{ 599 // Derivation: 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. 608 609 if (abs(p.z) > 1 || abs(-_near) < limits<T>::max() * abs(p.z)) 610 { 611 return radius * (-_near / p.z); 612 } 613 else 614 { 615 throw Iex::DivzeroExc 616 ("Bad call to Frustum::screenRadius: the magnitude of `p' " 617 "is too small"); 618 } 619 620 return radius * (-_near / p.z); 621} 622 623template<class T> 624T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const 625{ 626 if (abs(-_near) > 1 || abs(p.z) < limits<T>::max() * abs(-_near)) 627 { 628 return radius * (p.z / -_near); 629 } 630 else 631 { 632 throw Iex::DivzeroExc 633 ("Bad viewing frustum: the near clipping plane is too " 634 "close to zero"); 635 } 636} 637 638template<class T> 639void Frustum<T>::planes(Plane3<T> p[6]) 640{ 641 // 642 // Plane order: Top, Right, Bottom, Left, Near, Far. 643 // Normals point outwards. 644 // 645 646 if (! _orthographic) 647 { 648 Vec3<T> a( _left, _bottom, -_near); 649 Vec3<T> b( _left, _top, -_near); 650 Vec3<T> c( _right, _top, -_near); 651 Vec3<T> d( _right, _bottom, -_near); 652 Vec3<T> o(0,0,0); 653 654 p[0].set( o, c, b ); 655 p[1].set( o, d, c ); 656 p[2].set( o, a, d ); 657 p[3].set( o, b, a ); 658 } 659 else 660 { 661 p[0].set( Vec3<T>( 0, 1, 0), _top ); 662 p[1].set( Vec3<T>( 1, 0, 0), _right ); 663 p[2].set( Vec3<T>( 0,-1, 0),-_bottom ); 664 p[3].set( Vec3<T>(-1, 0, 0),-_left ); 665 } 666 p[4].set( Vec3<T>(0, 0, 1), -_near ); 667 p[5].set( Vec3<T>(0, 0,-1), _far ); 668} 669 670 671template<class T> 672void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M) 673{ 674 // 675 // Plane order: Top, Right, Bottom, Left, Near, Far. 676 // Normals point outwards. 677 // 678 679 Vec3<T> a = Vec3<T>( _left, _bottom, -_near) * M; 680 Vec3<T> b = Vec3<T>( _left, _top, -_near) * M; 681 Vec3<T> c = Vec3<T>( _right, _top, -_near) * M; 682 Vec3<T> d = Vec3<T>( _right, _bottom, -_near) * M; 683 if (! _orthographic) 684 { 685 double s = _far / double(_near); 686 T farLeft = (T) (s * _left); 687 T farRight = (T) (s * _right); 688 T farTop = (T) (s * _top); 689 T farBottom = (T) (s * _bottom); 690 Vec3<T> e = Vec3<T>( farLeft, farBottom, -_far) * M; 691 Vec3<T> f = Vec3<T>( farLeft, farTop, -_far) * M; 692 Vec3<T> g = Vec3<T>( farRight, farTop, -_far) * M; 693 Vec3<T> o = Vec3<T>(0,0,0) * M; 694 p[0].set( o, c, b ); 695 p[1].set( o, d, c ); 696 p[2].set( o, a, d ); 697 p[3].set( o, b, a ); 698 p[4].set( a, d, c ); 699 p[5].set( e, f, g ); 700 } 701 else 702 { 703 Vec3<T> e = Vec3<T>( _left, _bottom, -_far) * M; 704 Vec3<T> f = Vec3<T>( _left, _top, -_far) * M; 705 Vec3<T> g = Vec3<T>( _right, _top, -_far) * M; 706 Vec3<T> h = Vec3<T>( _right, _bottom, -_far) * M; 707 p[0].set( c, g, f ); 708 p[1].set( d, h, g ); 709 p[2].set( a, e, h ); 710 p[3].set( b, f, e ); 711 p[4].set( a, d, c ); 712 p[5].set( e, f, g ); 713 } 714} 715 716typedef Frustum<float> Frustumf; 717typedef Frustum<double> Frustumd; 718 719 720} // namespace Imath 721 722#endif 723