Path: blob/master/3rdparty/openexr/Imath/ImathFrustum.h
16337 views
///////////////////////////////////////////////////////////////////////////1//2// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas3// Digital Ltd. LLC4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions are9// met:10// * Redistributions of source code must retain the above copyright11// notice, this list of conditions and the following disclaimer.12// * Redistributions in binary form must reproduce the above13// copyright notice, this list of conditions and the following disclaimer14// in the documentation and/or other materials provided with the15// distribution.16// * Neither the name of Industrial Light & Magic nor the names of17// its contributors may be used to endorse or promote products derived18// from this software without specific prior written permission.19//20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31//32///////////////////////////////////////////////////////////////////////////33343536#ifndef INCLUDED_IMATHFRUSTUM_H37#define INCLUDED_IMATHFRUSTUM_H383940#include "ImathVec.h"41#include "ImathPlane.h"42#include "ImathLine.h"43#include "ImathMatrix.h"44#include "ImathLimits.h"45#include "ImathFun.h"46#include "IexMathExc.h"4748namespace Imath {4950//51// template class Frustum<T>52//53// The frustum is always located with the eye point at the54// origin facing down -Z. This makes the Frustum class55// compatable with OpenGL (or anything that assumes a camera56// looks down -Z, hence with a right-handed coordinate system)57// but not with RenderMan which assumes the camera looks down58// +Z. Additional functions are provided for conversion from59// and from various camera coordinate spaces.60//61// nearPlane/farPlane: near/far are keywords used by Microsoft's62// compiler, so we use nearPlane/farPlane instead to avoid63// issues.646566template<class T>67class Frustum68{69public:70Frustum();71Frustum(const Frustum &);72Frustum(T nearPlane, T farPlane, T left, T right, T top, T bottom, bool ortho=false);73Frustum(T nearPlane, T farPlane, T fovx, T fovy, T aspect);74virtual ~Frustum();7576//--------------------77// Assignment operator78//--------------------7980const Frustum &operator = (const Frustum &);8182//--------------------83// Operators: ==, !=84//--------------------8586bool operator == (const Frustum<T> &src) const;87bool operator != (const Frustum<T> &src) const;8889//--------------------------------------------------------90// Set functions change the entire state of the Frustum91//--------------------------------------------------------9293void set(T nearPlane, T farPlane,94T left, T right,95T top, T bottom,96bool ortho=false);9798void set(T nearPlane, T farPlane, T fovx, T fovy, T aspect);99100//------------------------------------------------------101// These functions modify an already valid frustum state102//------------------------------------------------------103104void modifyNearAndFar(T nearPlane, T farPlane);105void setOrthographic(bool);106107//--------------108// Access109//--------------110111bool orthographic() const { return _orthographic; }112T nearPlane() const { return _nearPlane; }113T hither() const { return _nearPlane; }114T farPlane() const { return _farPlane; }115T yon() const { return _farPlane; }116T left() const { return _left; }117T right() const { return _right; }118T bottom() const { return _bottom; }119T top() const { return _top; }120121//-----------------------------------------------------------------------122// Sets the planes in p to be the six bounding planes of the frustum, in123// the following order: top, right, bottom, left, near, far.124// Note that the planes have normals that point out of the frustum.125// The version of this routine that takes a matrix applies that matrix126// to transform the frustum before setting the planes.127//-----------------------------------------------------------------------128129void planes(Plane3<T> p[6]);130void planes(Plane3<T> p[6], const Matrix44<T> &M);131132//----------------------133// Derived Quantities134//----------------------135136T fovx() const;137T fovy() const;138T aspect() const;139Matrix44<T> projectionMatrix() const;140bool degenerate() const;141142//-----------------------------------------------------------------------143// Takes a rectangle in the screen space (i.e., -1 <= left <= right <= 1144// and -1 <= bottom <= top <= 1) of this Frustum, and returns a new145// Frustum whose near clipping-plane window is that rectangle in local146// space.147//-----------------------------------------------------------------------148149Frustum<T> window(T left, T right, T top, T bottom) const;150151//----------------------------------------------------------152// Projection is in screen space / Conversion from Z-Buffer153//----------------------------------------------------------154155Line3<T> projectScreenToRay( const Vec2<T> & ) const;156Vec2<T> projectPointToScreen( const Vec3<T> & ) const;157158T ZToDepth(long zval, long min, long max) const;159T normalizedZToDepth(T zval) const;160long DepthToZ(T depth, long zmin, long zmax) const;161162T worldRadius(const Vec3<T> &p, T radius) const;163T screenRadius(const Vec3<T> &p, T radius) const;164165166protected:167168Vec2<T> screenToLocal( const Vec2<T> & ) const;169Vec2<T> localToScreen( const Vec2<T> & ) const;170171protected:172T _nearPlane;173T _farPlane;174T _left;175T _right;176T _top;177T _bottom;178bool _orthographic;179};180181182template<class T>183inline Frustum<T>::Frustum()184{185set(T (0.1),186T (1000.0),187T (-1.0),188T (1.0),189T (1.0),190T (-1.0),191false);192}193194template<class T>195inline Frustum<T>::Frustum(const Frustum &f)196{197*this = f;198}199200template<class T>201inline Frustum<T>::Frustum(T n, T f, T l, T r, T t, T b, bool o)202{203set(n,f,l,r,t,b,o);204}205206template<class T>207inline Frustum<T>::Frustum(T nearPlane, T farPlane, T fovx, T fovy, T aspect)208{209set(nearPlane,farPlane,fovx,fovy,aspect);210}211212template<class T>213Frustum<T>::~Frustum()214{215}216217template<class T>218const Frustum<T> &219Frustum<T>::operator = (const Frustum &f)220{221_nearPlane = f._nearPlane;222_farPlane = f._farPlane;223_left = f._left;224_right = f._right;225_top = f._top;226_bottom = f._bottom;227_orthographic = f._orthographic;228229return *this;230}231232template <class T>233bool234Frustum<T>::operator == (const Frustum<T> &src) const235{236return237_nearPlane == src._nearPlane &&238_farPlane == src._farPlane &&239_left == src._left &&240_right == src._right &&241_top == src._top &&242_bottom == src._bottom &&243_orthographic == src._orthographic;244}245246template <class T>247inline bool248Frustum<T>::operator != (const Frustum<T> &src) const249{250return !operator== (src);251}252253template<class T>254void Frustum<T>::set(T n, T f, T l, T r, T t, T b, bool o)255{256_nearPlane = n;257_farPlane = f;258_left = l;259_right = r;260_bottom = b;261_top = t;262_orthographic = o;263}264265template<class T>266void Frustum<T>::modifyNearAndFar(T n, T f)267{268if ( _orthographic )269{270_nearPlane = n;271}272else273{274Line3<T> lowerLeft( Vec3<T>(0,0,0), Vec3<T>(_left,_bottom,-_nearPlane) );275Line3<T> upperRight( Vec3<T>(0,0,0), Vec3<T>(_right,_top,-_nearPlane) );276Plane3<T> nearPlane( Vec3<T>(0,0,-1), n );277278Vec3<T> ll,ur;279nearPlane.intersect(lowerLeft,ll);280nearPlane.intersect(upperRight,ur);281282_left = ll.x;283_right = ur.x;284_top = ur.y;285_bottom = ll.y;286_nearPlane = n;287_farPlane = f;288}289290_farPlane = f;291}292293template<class T>294void Frustum<T>::setOrthographic(bool ortho)295{296_orthographic = ortho;297}298299template<class T>300void Frustum<T>::set(T nearPlane, T farPlane, T fovx, T fovy, T aspect)301{302if (fovx != 0 && fovy != 0)303throw Iex::ArgExc ("fovx and fovy cannot both be non-zero.");304305const T two = static_cast<T>(2);306307if (fovx != 0)308{309_right = nearPlane * Math<T>::tan(fovx / two);310_left = -_right;311_top = ((_right - _left) / aspect) / two;312_bottom = -_top;313}314else315{316_top = nearPlane * Math<T>::tan(fovy / two);317_bottom = -_top;318_right = (_top - _bottom) * aspect / two;319_left = -_right;320}321_nearPlane = nearPlane;322_farPlane = farPlane;323_orthographic = false;324}325326template<class T>327T Frustum<T>::fovx() const328{329return Math<T>::atan2(_right,_nearPlane) - Math<T>::atan2(_left,_nearPlane);330}331332template<class T>333T Frustum<T>::fovy() const334{335return Math<T>::atan2(_top,_nearPlane) - Math<T>::atan2(_bottom,_nearPlane);336}337338template<class T>339T Frustum<T>::aspect() const340{341T rightMinusLeft = _right-_left;342T topMinusBottom = _top-_bottom;343344if (abs(topMinusBottom) < 1 &&345abs(rightMinusLeft) > limits<T>::max() * abs(topMinusBottom))346{347throw Iex::DivzeroExc ("Bad viewing frustum: "348"aspect ratio cannot be computed.");349}350351return rightMinusLeft / topMinusBottom;352}353354template<class T>355Matrix44<T> Frustum<T>::projectionMatrix() const356{357T rightPlusLeft = _right+_left;358T rightMinusLeft = _right-_left;359360T topPlusBottom = _top+_bottom;361T topMinusBottom = _top-_bottom;362363T farPlusNear = _farPlane+_nearPlane;364T farMinusNear = _farPlane-_nearPlane;365366if ((abs(rightMinusLeft) < 1 &&367abs(rightPlusLeft) > limits<T>::max() * abs(rightMinusLeft)) ||368(abs(topMinusBottom) < 1 &&369abs(topPlusBottom) > limits<T>::max() * abs(topMinusBottom)) ||370(abs(farMinusNear) < 1 &&371abs(farPlusNear) > limits<T>::max() * abs(farMinusNear)))372{373throw Iex::DivzeroExc ("Bad viewing frustum: "374"projection matrix cannot be computed.");375}376377if ( _orthographic )378{379T tx = -rightPlusLeft / rightMinusLeft;380T ty = -topPlusBottom / topMinusBottom;381T tz = -farPlusNear / farMinusNear;382383if ((abs(rightMinusLeft) < 1 &&3842 > limits<T>::max() * abs(rightMinusLeft)) ||385(abs(topMinusBottom) < 1 &&3862 > limits<T>::max() * abs(topMinusBottom)) ||387(abs(farMinusNear) < 1 &&3882 > limits<T>::max() * abs(farMinusNear)))389{390throw Iex::DivzeroExc ("Bad viewing frustum: "391"projection matrix cannot be computed.");392}393394T A = 2 / rightMinusLeft;395T B = 2 / topMinusBottom;396T C = -2 / farMinusNear;397398return Matrix44<T>( A, 0, 0, 0,3990, B, 0, 0,4000, 0, C, 0,401tx, ty, tz, 1.f );402}403else404{405T A = rightPlusLeft / rightMinusLeft;406T B = topPlusBottom / topMinusBottom;407T C = -farPlusNear / farMinusNear;408409T farTimesNear = -2 * _farPlane * _nearPlane;410if (abs(farMinusNear) < 1 &&411abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))412{413throw Iex::DivzeroExc ("Bad viewing frustum: "414"projection matrix cannot be computed.");415}416417T D = farTimesNear / farMinusNear;418419T twoTimesNear = 2 * _nearPlane;420421if ((abs(rightMinusLeft) < 1 &&422abs(twoTimesNear) > limits<T>::max() * abs(rightMinusLeft)) ||423(abs(topMinusBottom) < 1 &&424abs(twoTimesNear) > limits<T>::max() * abs(topMinusBottom)))425{426throw Iex::DivzeroExc ("Bad viewing frustum: "427"projection matrix cannot be computed.");428}429430T E = twoTimesNear / rightMinusLeft;431T F = twoTimesNear / topMinusBottom;432433return Matrix44<T>( E, 0, 0, 0,4340, F, 0, 0,435A, B, C, -1,4360, 0, D, 0 );437}438}439440template<class T>441bool Frustum<T>::degenerate() const442{443return (_nearPlane == _farPlane) ||444(_left == _right) ||445(_top == _bottom);446}447448template<class T>449Frustum<T> Frustum<T>::window(T l, T r, T t, T b) const450{451// move it to 0->1 space452453Vec2<T> bl = screenToLocal( Vec2<T>(l,b) );454Vec2<T> tr = screenToLocal( Vec2<T>(r,t) );455456return Frustum<T>(_nearPlane, _farPlane, bl.x, tr.x, tr.y, bl.y, _orthographic);457}458459460template<class T>461Vec2<T> Frustum<T>::screenToLocal(const Vec2<T> &s) const462{463return Vec2<T>( _left + (_right-_left) * (1.f+s.x) / 2.f,464_bottom + (_top-_bottom) * (1.f+s.y) / 2.f );465}466467template<class T>468Vec2<T> Frustum<T>::localToScreen(const Vec2<T> &p) const469{470T leftPlusRight = _left - T (2) * p.x + _right;471T leftMinusRight = _left-_right;472T bottomPlusTop = _bottom - T (2) * p.y + _top;473T bottomMinusTop = _bottom-_top;474475if ((abs(leftMinusRight) < T (1) &&476abs(leftPlusRight) > limits<T>::max() * abs(leftMinusRight)) ||477(abs(bottomMinusTop) < T (1) &&478abs(bottomPlusTop) > limits<T>::max() * abs(bottomMinusTop)))479{480throw Iex::DivzeroExc481("Bad viewing frustum: "482"local-to-screen transformation cannot be computed");483}484485return Vec2<T>( leftPlusRight / leftMinusRight,486bottomPlusTop / bottomMinusTop );487}488489template<class T>490Line3<T> Frustum<T>::projectScreenToRay(const Vec2<T> &p) const491{492Vec2<T> point = screenToLocal(p);493if (orthographic())494return Line3<T>( Vec3<T>(point.x,point.y, 0.0),495Vec3<T>(point.x,point.y,-_nearPlane));496else497return Line3<T>( Vec3<T>(0, 0, 0), Vec3<T>(point.x,point.y,-_nearPlane));498}499500template<class T>501Vec2<T> Frustum<T>::projectPointToScreen(const Vec3<T> &point) const502{503if (orthographic() || point.z == T (0))504return localToScreen( Vec2<T>( point.x, point.y ) );505else506return localToScreen( Vec2<T>( point.x * _nearPlane / -point.z,507point.y * _nearPlane / -point.z ) );508}509510template<class T>511T Frustum<T>::ZToDepth(long zval,long zmin,long zmax) const512{513int zdiff = zmax - zmin;514515if (zdiff == 0)516{517throw Iex::DivzeroExc518("Bad call to Frustum::ZToDepth: zmax == zmin");519}520521if ( zval > zmax+1 ) zval -= zdiff;522523T fzval = (T(zval) - T(zmin)) / T(zdiff);524return normalizedZToDepth(fzval);525}526527template<class T>528T Frustum<T>::normalizedZToDepth(T zval) const529{530T Zp = zval * 2.0 - 1;531532if ( _orthographic )533{534return -(Zp*(_farPlane-_nearPlane) + (_farPlane+_nearPlane))/2;535}536else537{538T farTimesNear = 2 * _farPlane * _nearPlane;539T farMinusNear = Zp * (_farPlane - _nearPlane) - _farPlane - _nearPlane;540541if (abs(farMinusNear) < 1 &&542abs(farTimesNear) > limits<T>::max() * abs(farMinusNear))543{544throw Iex::DivzeroExc545("Frustum::normalizedZToDepth cannot be computed. The "546"near and far clipping planes of the viewing frustum "547"may be too close to each other");548}549550return farTimesNear / farMinusNear;551}552}553554template<class T>555long Frustum<T>::DepthToZ(T depth,long zmin,long zmax) const556{557long zdiff = zmax - zmin;558T farMinusNear = _farPlane-_nearPlane;559560if ( _orthographic )561{562T farPlusNear = 2*depth + _farPlane + _nearPlane;563564if (abs(farMinusNear) < 1 &&565abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))566{567throw Iex::DivzeroExc568("Bad viewing frustum: near and far clipping planes "569"are too close to each other");570}571572T Zp = -farPlusNear/farMinusNear;573return long(0.5*(Zp+1)*zdiff) + zmin;574}575else576{577// Perspective578579T farTimesNear = 2*_farPlane*_nearPlane;580if (abs(depth) < 1 &&581abs(farTimesNear) > limits<T>::max() * abs(depth))582{583throw Iex::DivzeroExc584("Bad call to DepthToZ function: value of `depth' "585"is too small");586}587588T farPlusNear = farTimesNear/depth + _farPlane + _nearPlane;589if (abs(farMinusNear) < 1 &&590abs(farPlusNear) > limits<T>::max() * abs(farMinusNear))591{592throw Iex::DivzeroExc593("Bad viewing frustum: near and far clipping planes "594"are too close to each other");595}596597T Zp = farPlusNear/farMinusNear;598return long(0.5*(Zp+1)*zdiff) + zmin;599}600}601602template<class T>603T Frustum<T>::screenRadius(const Vec3<T> &p, T radius) const604{605// Derivation:606// Consider X-Z plane.607// X coord of projection of p = xp = p.x * (-_nearPlane / p.z)608// Let q be p + (radius, 0, 0).609// X coord of projection of q = xq = (p.x - radius) * (-_nearPlane / p.z)610// X coord of projection of segment from p to q = r = xp - xq611// = radius * (-_nearPlane / p.z)612// A similar analysis holds in the Y-Z plane.613// So r is the quantity we want to return.614615if (abs(p.z) > 1 || abs(-_nearPlane) < limits<T>::max() * abs(p.z))616{617return radius * (-_nearPlane / p.z);618}619else620{621throw Iex::DivzeroExc622("Bad call to Frustum::screenRadius: the magnitude of `p' "623"is too small");624}625626return radius * (-_nearPlane / p.z);627}628629template<class T>630T Frustum<T>::worldRadius(const Vec3<T> &p, T radius) const631{632if (abs(-_nearPlane) > 1 || abs(p.z) < limits<T>::max() * abs(-_nearPlane))633{634return radius * (p.z / -_nearPlane);635}636else637{638throw Iex::DivzeroExc639("Bad viewing frustum: the near clipping plane is too "640"close to zero");641}642}643644template<class T>645void Frustum<T>::planes(Plane3<T> p[6])646{647//648// Plane order: Top, Right, Bottom, Left, Near, Far.649// Normals point outwards.650//651652if (! _orthographic)653{654Vec3<T> a( _left, _bottom, -_nearPlane);655Vec3<T> b( _left, _top, -_nearPlane);656Vec3<T> c( _right, _top, -_nearPlane);657Vec3<T> d( _right, _bottom, -_nearPlane);658Vec3<T> o(0,0,0);659660p[0].set( o, c, b );661p[1].set( o, d, c );662p[2].set( o, a, d );663p[3].set( o, b, a );664}665else666{667p[0].set( Vec3<T>( 0, 1, 0), _top );668p[1].set( Vec3<T>( 1, 0, 0), _right );669p[2].set( Vec3<T>( 0,-1, 0),-_bottom );670p[3].set( Vec3<T>(-1, 0, 0),-_left );671}672p[4].set( Vec3<T>(0, 0, 1), -_nearPlane );673p[5].set( Vec3<T>(0, 0,-1), _farPlane );674}675676677template<class T>678void Frustum<T>::planes(Plane3<T> p[6], const Matrix44<T> &M)679{680//681// Plane order: Top, Right, Bottom, Left, Near, Far.682// Normals point outwards.683//684685Vec3<T> a = Vec3<T>( _left, _bottom, -_nearPlane) * M;686Vec3<T> b = Vec3<T>( _left, _top, -_nearPlane) * M;687Vec3<T> c = Vec3<T>( _right, _top, -_nearPlane) * M;688Vec3<T> d = Vec3<T>( _right, _bottom, -_nearPlane) * M;689if (! _orthographic)690{691double s = _farPlane / double(_nearPlane);692T farLeft = (T) (s * _left);693T farRight = (T) (s * _right);694T farTop = (T) (s * _top);695T farBottom = (T) (s * _bottom);696Vec3<T> e = Vec3<T>( farLeft, farBottom, -_farPlane) * M;697Vec3<T> f = Vec3<T>( farLeft, farTop, -_farPlane) * M;698Vec3<T> g = Vec3<T>( farRight, farTop, -_farPlane) * M;699Vec3<T> o = Vec3<T>(0,0,0) * M;700p[0].set( o, c, b );701p[1].set( o, d, c );702p[2].set( o, a, d );703p[3].set( o, b, a );704p[4].set( a, d, c );705p[5].set( e, f, g );706}707else708{709Vec3<T> e = Vec3<T>( _left, _bottom, -_farPlane) * M;710Vec3<T> f = Vec3<T>( _left, _top, -_farPlane) * M;711Vec3<T> g = Vec3<T>( _right, _top, -_farPlane) * M;712Vec3<T> h = Vec3<T>( _right, _bottom, -_farPlane) * M;713p[0].set( c, g, f );714p[1].set( d, h, g );715p[2].set( a, e, h );716p[3].set( b, f, e );717p[4].set( a, d, c );718p[5].set( e, f, g );719}720}721722typedef Frustum<float> Frustumf;723typedef Frustum<double> Frustumd;724725726} // namespace Imath727728729#if defined _WIN32 || defined _WIN64730#ifdef _redef_near731#define near732#endif733#ifdef _redef_far734#define far735#endif736#endif737738#endif739740741