// // SPDX-License-Identifier: BSD-3-Clause // Copyright Contributors to the OpenEXR Project. // // // A viewing frustum class // #ifndef INCLUDED_IMATHFRUSTUM_H #define INCLUDED_IMATHFRUSTUM_H #include "ImathExport.h" #include "ImathNamespace.h" #include "ImathFun.h" #include "ImathLine.h" #include "ImathMatrix.h" #include "ImathPlane.h" #include "ImathVec.h" IMATH_INTERNAL_NAMESPACE_HEADER_ENTER /// /// Template class `Frustum` /// /// The frustum is always located with the eye point at the origin /// facing down -Z. This makes the Frustum class compatable with /// OpenGL (or anything that assumes a camera looks down -Z, hence /// with a right-handed coordinate system) but not with RenderMan /// which assumes the camera looks down +Z. Additional functions are /// provided for conversion from and from various camera coordinate /// spaces. /// /// nearPlane/farPlane: near/far are keywords used by Microsoft's /// compiler, so we use nearPlane/farPlane instead to avoid /// issues. template class IMATH_EXPORT_TEMPLATE_TYPE Frustum { public: /// @{ /// @name Constructors and Assignment /// /// Initialize with default values: /// near=0.1, far=1000.0, left=-1.0, right=1.0, top=1.0, bottom=-1.0, ortho=false IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Frustum() IMATH_NOEXCEPT; /// Copy constructor IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Frustum (const Frustum&) IMATH_NOEXCEPT; /// Initialize to specific values IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Frustum (T nearPlane, T farPlane, T left, T right, T top, T bottom, bool ortho = false) IMATH_NOEXCEPT; /// Initialize with fov and aspect IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Frustum (T nearPlane, T farPlane, T fovx, T fovy, T aspect) IMATH_NOEXCEPT; /// Destructor virtual ~Frustum() IMATH_NOEXCEPT; /// Component-wise assignment IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Frustum& operator= (const Frustum&) IMATH_NOEXCEPT; /// @} /// @{ /// @name Comparison /// Equality IMATH_HOSTDEVICE constexpr bool operator== (const Frustum& src) const IMATH_NOEXCEPT; /// Inequality IMATH_HOSTDEVICE constexpr bool operator!= (const Frustum& src) const IMATH_NOEXCEPT; /// @} /// @{ /// @name Query /// Return true if the frustum is orthographic, false if perspective IMATH_HOSTDEVICE constexpr bool orthographic() const IMATH_NOEXCEPT { return _orthographic; } /// Return the near clipping plane IMATH_HOSTDEVICE constexpr T nearPlane() const IMATH_NOEXCEPT { return _nearPlane; } /// Return the near clipping plane IMATH_HOSTDEVICE constexpr T hither() const IMATH_NOEXCEPT { return _nearPlane; } /// Return the far clipping plane IMATH_HOSTDEVICE constexpr T farPlane() const IMATH_NOEXCEPT { return _farPlane; } /// Return the far clipping plane IMATH_HOSTDEVICE constexpr T yon() const IMATH_NOEXCEPT { return _farPlane; } /// Return the left of the frustum IMATH_HOSTDEVICE constexpr T left() const IMATH_NOEXCEPT { return _left; } /// Return the right of the frustum IMATH_HOSTDEVICE constexpr T right() const IMATH_NOEXCEPT { return _right; } /// Return the bottom of the frustum IMATH_HOSTDEVICE constexpr T bottom() const IMATH_NOEXCEPT { return _bottom; } /// Return the top of the frustum IMATH_HOSTDEVICE constexpr T top() const IMATH_NOEXCEPT { return _top; } /// Return the field of view in X IMATH_HOSTDEVICE constexpr T fovx() const IMATH_NOEXCEPT; /// Return the field of view in Y IMATH_HOSTDEVICE constexpr T fovy() const IMATH_NOEXCEPT; /// Return the aspect ratio IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T aspect() const IMATH_NOEXCEPT; /// Return the aspect ratio. Throw an exception if the aspect /// ratio is undefined. IMATH_CONSTEXPR14 T aspectExc() const; /// Return the project matrix that the frustum defines IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Matrix44 projectionMatrix() const IMATH_NOEXCEPT; /// Return the project matrix that the frustum defines. Throw an /// exception if the frustum is degenerate. IMATH_CONSTEXPR14 Matrix44 projectionMatrixExc() const; /// Return true if the frustum is degenerate. IMATH_HOSTDEVICE constexpr bool degenerate() const IMATH_NOEXCEPT; /// @} /// @{ /// @name Set Value /// Set functions change the entire state of the Frustum IMATH_HOSTDEVICE void set (T nearPlane, T farPlane, T left, T right, T top, T bottom, bool ortho = false) IMATH_NOEXCEPT; /// Set functions change the entire state of the Frustum using /// field of view and aspect ratio IMATH_HOSTDEVICE void set (T nearPlane, T farPlane, T fovx, T fovy, T aspect) IMATH_NOEXCEPT; /// Set functions change the entire state of the Frustum using /// field of view and aspect ratio. Throw an exception if `fovx` /// and/or `fovy` are invalid. void setExc (T nearPlane, T farPlane, T fovx, T fovy, T aspect); /// Set the near and far clipping planes IMATH_HOSTDEVICE void modifyNearAndFar (T nearPlane, T farPlane) IMATH_NOEXCEPT; /// Set the ortographic state IMATH_HOSTDEVICE void setOrthographic (bool) IMATH_NOEXCEPT; /// Set the planes in p to be the six bounding planes of the frustum, in /// the following order: top, right, bottom, left, near, far. /// Note that the planes have normals that point out of the frustum. IMATH_HOSTDEVICE void planes (Plane3 p[6]) const IMATH_NOEXCEPT; /// Set the planes in p to be the six bounding planes of the /// frustum, in the following order: top, right, bottom, left, /// near, far. Note that the planes have normals that point out /// of the frustum. Apply the given matrix to transform the /// frustum before setting the planes. IMATH_HOSTDEVICE void planes (Plane3 p[6], const Matrix44& M) const IMATH_NOEXCEPT; /// Takes a rectangle in the screen space (i.e., -1 <= left <= right <= 1 /// and -1 <= bottom <= top <= 1) of this Frustum, and returns a new /// Frustum whose near clipping-plane window is that rectangle in local /// space. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 IMATH_HOSTDEVICE Frustum window (T left, T right, T top, T bottom) const IMATH_NOEXCEPT; /// @} /// @{ /// @name Utility Methods /// Project a point in screen spaced to 3d ray IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Line3 projectScreenToRay (const Vec2&) const IMATH_NOEXCEPT; /// Project a 3D point into screen coordinates IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Vec2 projectPointToScreen (const Vec3&) const IMATH_NOEXCEPT; /// Project a 3D point into screen coordinates. Throw an /// exception if the point cannot be projected. IMATH_CONSTEXPR14 Vec2 projectPointToScreenExc (const Vec3&) const; /// Map a z value to its depth in the frustum. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T ZToDepth (long zval, long min, long max) const IMATH_NOEXCEPT; /// Map a z value to its depth in the frustum. IMATH_CONSTEXPR14 T ZToDepthExc (long zval, long min, long max) const; /// Map a normalized z value to its depth in the frustum. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T normalizedZToDepth (T zval) const IMATH_NOEXCEPT; /// Map a normalized z value to its depth in the frustum. Throw an /// exception on error. IMATH_CONSTEXPR14 T normalizedZToDepthExc (T zval) const; /// Map depth to z value. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 long DepthToZ (T depth, long zmin, long zmax) const IMATH_NOEXCEPT; /// Map depth to z value. Throw an exception on error. IMATH_CONSTEXPR14 long DepthToZExc (T depth, long zmin, long zmax) const; /// Compute worldRadius IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T worldRadius (const Vec3& p, T radius) const IMATH_NOEXCEPT; /// Compute worldRadius. Throw an exception on error. IMATH_CONSTEXPR14 T worldRadiusExc (const Vec3& p, T radius) const; /// Compute screen radius IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T screenRadius (const Vec3& p, T radius) const IMATH_NOEXCEPT; /// Compute screen radius. Throw an exception on error. IMATH_CONSTEXPR14 T screenRadiusExc (const Vec3& p, T radius) const; /// @} protected: /// Map point from screen space to local space IMATH_HOSTDEVICE constexpr Vec2 screenToLocal (const Vec2&) const IMATH_NOEXCEPT; /// Map point from local space to screen space IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Vec2 localToScreen (const Vec2&) const IMATH_NOEXCEPT; /// Map point from local space to screen space. Throw an exception /// on error. IMATH_CONSTEXPR14 Vec2 localToScreenExc (const Vec2&) const; protected: /// @cond Doxygen_Suppress T _nearPlane; T _farPlane; T _left; T _right; T _top; T _bottom; bool _orthographic; /// @endcond }; template IMATH_CONSTEXPR14 inline Frustum::Frustum() IMATH_NOEXCEPT { set (T (0.1), T (1000.0), T (-1.0), T (1.0), T (1.0), T (-1.0), false); } template IMATH_CONSTEXPR14 inline Frustum::Frustum (const Frustum& f) IMATH_NOEXCEPT { *this = f; } template IMATH_CONSTEXPR14 inline Frustum::Frustum (T n, T f, T l, T r, T t, T b, bool o) IMATH_NOEXCEPT { set (n, f, l, r, t, b, o); } template IMATH_CONSTEXPR14 inline Frustum::Frustum (T nearPlane, T farPlane, T fovx, T fovy, T aspect) IMATH_NOEXCEPT { set (nearPlane, farPlane, fovx, fovy, aspect); } template Frustum::~Frustum() IMATH_NOEXCEPT {} template IMATH_CONSTEXPR14 inline const Frustum& Frustum::operator= (const Frustum& f) IMATH_NOEXCEPT { _nearPlane = f._nearPlane; _farPlane = f._farPlane; _left = f._left; _right = f._right; _top = f._top; _bottom = f._bottom; _orthographic = f._orthographic; return *this; } template constexpr inline bool Frustum::operator== (const Frustum& src) const IMATH_NOEXCEPT { return _nearPlane == src._nearPlane && _farPlane == src._farPlane && _left == src._left && _right == src._right && _top == src._top && _bottom == src._bottom && _orthographic == src._orthographic; } template constexpr inline bool Frustum::operator!= (const Frustum& src) const IMATH_NOEXCEPT { return !operator== (src); } template inline void Frustum::set (T n, T f, T l, T r, T t, T b, bool o) IMATH_NOEXCEPT { _nearPlane = n; _farPlane = f; _left = l; _right = r; _bottom = b; _top = t; _orthographic = o; } template inline void Frustum::modifyNearAndFar (T n, T f) IMATH_NOEXCEPT { if (_orthographic) { _nearPlane = n; } else { Line3 lowerLeft (Vec3 (0, 0, 0), Vec3 (_left, _bottom, -_nearPlane)); Line3 upperRight (Vec3 (0, 0, 0), Vec3 (_right, _top, -_nearPlane)); Plane3 nearPlane (Vec3 (0, 0, -1), n); Vec3 ll = Vec3 (0, 0, 0); Vec3 ur = Vec3 (0, 0, 0); nearPlane.intersect (lowerLeft, ll); nearPlane.intersect (upperRight, ur); _left = ll.x; _right = ur.x; _top = ur.y; _bottom = ll.y; _nearPlane = n; _farPlane = f; } _farPlane = f; } template inline void Frustum::setOrthographic (bool ortho) IMATH_NOEXCEPT { _orthographic = ortho; } template inline void Frustum::setExc (T nearPlane, T farPlane, T fovx, T fovy, T aspect) { if (fovx != T (0) && fovy != T (0)) throw std::domain_error ("fovx and fovy cannot both be non-zero."); const T two = static_cast (2); if (fovx != T (0)) { _right = nearPlane * std::tan (fovx / two); _left = -_right; _top = ((_right - _left) / aspect) / two; _bottom = -_top; } else { _top = nearPlane * std::tan (fovy / two); _bottom = -_top; _right = (_top - _bottom) * aspect / two; _left = -_right; } _nearPlane = nearPlane; _farPlane = farPlane; _orthographic = false; } template inline void Frustum::set (T nearPlane, T farPlane, T fovx, T fovy, T aspect) IMATH_NOEXCEPT { const T two = static_cast (2); if (fovx != T (0)) { _right = nearPlane * std::tan (fovx / two); _left = -_right; _top = ((_right - _left) / aspect) / two; _bottom = -_top; } else { _top = nearPlane * std::tan (fovy / two); _bottom = -_top; _right = (_top - _bottom) * aspect / two; _left = -_right; } _nearPlane = nearPlane; _farPlane = farPlane; _orthographic = false; } template constexpr inline T Frustum::fovx() const IMATH_NOEXCEPT { return std::atan2 (_right, _nearPlane) - std::atan2 (_left, _nearPlane); } template constexpr inline T Frustum::fovy() const IMATH_NOEXCEPT { return std::atan2 (_top, _nearPlane) - std::atan2 (_bottom, _nearPlane); } template IMATH_CONSTEXPR14 inline T Frustum::aspectExc() const { T rightMinusLeft = _right - _left; T topMinusBottom = _top - _bottom; if (abs (topMinusBottom) < T (1) && abs (rightMinusLeft) > std::numeric_limits::max() * abs (topMinusBottom)) { throw std::domain_error ("Bad viewing frustum: " "aspect ratio cannot be computed."); } return rightMinusLeft / topMinusBottom; } template IMATH_CONSTEXPR14 inline T Frustum::aspect() const IMATH_NOEXCEPT { T rightMinusLeft = _right - _left; T topMinusBottom = _top - _bottom; return rightMinusLeft / topMinusBottom; } template IMATH_CONSTEXPR14 inline Matrix44 Frustum::projectionMatrixExc() const { T rightPlusLeft = _right + _left; T rightMinusLeft = _right - _left; T topPlusBottom = _top + _bottom; T topMinusBottom = _top - _bottom; T farPlusNear = _farPlane + _nearPlane; T farMinusNear = _farPlane - _nearPlane; if ((abs (rightMinusLeft) < T (1) && abs (rightPlusLeft) > std::numeric_limits::max() * abs (rightMinusLeft)) || (abs (topMinusBottom) < T (1) && abs (topPlusBottom) > std::numeric_limits::max() * abs (topMinusBottom)) || (abs (farMinusNear) < 1 && abs (farPlusNear) > std::numeric_limits::max() * abs (farMinusNear))) { throw std::domain_error ("Bad viewing frustum: " "projection matrix cannot be computed."); } if (_orthographic) { T tx = -rightPlusLeft / rightMinusLeft; T ty = -topPlusBottom / topMinusBottom; T tz = -farPlusNear / farMinusNear; if ((abs (rightMinusLeft) < T (1) && T (2) > std::numeric_limits::max() * abs (rightMinusLeft)) || (abs (topMinusBottom) < T (1) && T (2) > std::numeric_limits::max() * abs (topMinusBottom)) || (abs (farMinusNear) < T (1) && T (2) > std::numeric_limits::max() * abs (farMinusNear))) { throw std::domain_error ("Bad viewing frustum: " "projection matrix cannot be computed."); } T A = T (2) / rightMinusLeft; T B = T (2) / topMinusBottom; T C = T (-2) / farMinusNear; return Matrix44 (A, 0, 0, 0, 0, B, 0, 0, 0, 0, C, 0, tx, ty, tz, 1.f); } else { T A = rightPlusLeft / rightMinusLeft; T B = topPlusBottom / topMinusBottom; T C = -farPlusNear / farMinusNear; T farTimesNear = T (-2) * _farPlane * _nearPlane; if (abs (farMinusNear) < T (1) && abs (farTimesNear) > std::numeric_limits::max() * abs (farMinusNear)) { throw std::domain_error ("Bad viewing frustum: " "projection matrix cannot be computed."); } T D = farTimesNear / farMinusNear; T twoTimesNear = T (2) * _nearPlane; if ((abs (rightMinusLeft) < T (1) && abs (twoTimesNear) > std::numeric_limits::max() * abs (rightMinusLeft)) || (abs (topMinusBottom) < T (1) && abs (twoTimesNear) > std::numeric_limits::max() * abs (topMinusBottom))) { throw std::domain_error ("Bad viewing frustum: " "projection matrix cannot be computed."); } T E = twoTimesNear / rightMinusLeft; T F = twoTimesNear / topMinusBottom; return Matrix44 (E, 0, 0, 0, 0, F, 0, 0, A, B, C, -1, 0, 0, D, 0); } } template IMATH_CONSTEXPR14 inline Matrix44 Frustum::projectionMatrix() const IMATH_NOEXCEPT { T rightPlusLeft = _right + _left; T rightMinusLeft = _right - _left; T topPlusBottom = _top + _bottom; T topMinusBottom = _top - _bottom; T farPlusNear = _farPlane + _nearPlane; T farMinusNear = _farPlane - _nearPlane; if (_orthographic) { T tx = -rightPlusLeft / rightMinusLeft; T ty = -topPlusBottom / topMinusBottom; T tz = -farPlusNear / farMinusNear; T A = T (2) / rightMinusLeft; T B = T (2) / topMinusBottom; T C = T (-2) / farMinusNear; return Matrix44 (A, 0, 0, 0, 0, B, 0, 0, 0, 0, C, 0, tx, ty, tz, 1.f); } else { T A = rightPlusLeft / rightMinusLeft; T B = topPlusBottom / topMinusBottom; T C = -farPlusNear / farMinusNear; T farTimesNear = T (-2) * _farPlane * _nearPlane; T D = farTimesNear / farMinusNear; T twoTimesNear = T (2) * _nearPlane; T E = twoTimesNear / rightMinusLeft; T F = twoTimesNear / topMinusBottom; return Matrix44 (E, 0, 0, 0, 0, F, 0, 0, A, B, C, -1, 0, 0, D, 0); } } template constexpr inline bool Frustum::degenerate() const IMATH_NOEXCEPT { return (_nearPlane == _farPlane) || (_left == _right) || (_top == _bottom); } template IMATH_CONSTEXPR14 inline Frustum Frustum::window (T l, T r, T t, T b) const IMATH_NOEXCEPT { // move it to 0->1 space Vec2 bl = screenToLocal (Vec2 (l, b)); Vec2 tr = screenToLocal (Vec2 (r, t)); return Frustum (_nearPlane, _farPlane, bl.x, tr.x, tr.y, bl.y, _orthographic); } template constexpr inline Vec2 Frustum::screenToLocal (const Vec2& s) const IMATH_NOEXCEPT { return Vec2 (_left + (_right - _left) * (1.f + s.x) / 2.f, _bottom + (_top - _bottom) * (1.f + s.y) / 2.f); } template IMATH_CONSTEXPR14 inline Vec2 Frustum::localToScreenExc (const Vec2& p) const { T leftPlusRight = _left - T (2) * p.x + _right; T leftMinusRight = _left - _right; T bottomPlusTop = _bottom - T (2) * p.y + _top; T bottomMinusTop = _bottom - _top; if ((abs (leftMinusRight) < T (1) && abs (leftPlusRight) > std::numeric_limits::max() * abs (leftMinusRight)) || (abs (bottomMinusTop) < T (1) && abs (bottomPlusTop) > std::numeric_limits::max() * abs (bottomMinusTop))) { throw std::domain_error ("Bad viewing frustum: " "local-to-screen transformation cannot be computed"); } return Vec2 (leftPlusRight / leftMinusRight, bottomPlusTop / bottomMinusTop); } template IMATH_CONSTEXPR14 inline Vec2 Frustum::localToScreen (const Vec2& p) const IMATH_NOEXCEPT { T leftPlusRight = _left - T (2) * p.x + _right; T leftMinusRight = _left - _right; T bottomPlusTop = _bottom - T (2) * p.y + _top; T bottomMinusTop = _bottom - _top; return Vec2 (leftPlusRight / leftMinusRight, bottomPlusTop / bottomMinusTop); } template IMATH_CONSTEXPR14 inline Line3 Frustum::projectScreenToRay (const Vec2& p) const IMATH_NOEXCEPT { Vec2 point = screenToLocal (p); if (orthographic()) return Line3 (Vec3 (point.x, point.y, 0.0), Vec3 (point.x, point.y, -1.0)); else return Line3 (Vec3 (0, 0, 0), Vec3 (point.x, point.y, -_nearPlane)); } template IMATH_CONSTEXPR14 Vec2 Frustum::projectPointToScreenExc (const Vec3& point) const { if (orthographic() || point.z == T (0)) return localToScreenExc (Vec2 (point.x, point.y)); else return localToScreenExc ( Vec2 (point.x * _nearPlane / -point.z, point.y * _nearPlane / -point.z)); } template IMATH_CONSTEXPR14 Vec2 Frustum::projectPointToScreen (const Vec3& point) const IMATH_NOEXCEPT { if (orthographic() || point.z == T (0)) return localToScreen (Vec2 (point.x, point.y)); else return localToScreen ( Vec2 (point.x * _nearPlane / -point.z, point.y * _nearPlane / -point.z)); } template IMATH_CONSTEXPR14 T Frustum::ZToDepthExc (long zval, long zmin, long zmax) const { int zdiff = zmax - zmin; if (zdiff == 0) { throw std::domain_error ("Bad call to Frustum::ZToDepth: zmax == zmin"); } if (zval > zmax + 1) zval -= zdiff; T fzval = (T (zval) - T (zmin)) / T (zdiff); return normalizedZToDepthExc (fzval); } template IMATH_CONSTEXPR14 T Frustum::ZToDepth (long zval, long zmin, long zmax) const IMATH_NOEXCEPT { int zdiff = zmax - zmin; if (zval > zmax + 1) zval -= zdiff; T fzval = (T (zval) - T (zmin)) / T (zdiff); return normalizedZToDepth (fzval); } template IMATH_CONSTEXPR14 T Frustum::normalizedZToDepthExc (T zval) const { T Zp = zval * T (2) - T (1); if (_orthographic) { return -(Zp * (_farPlane - _nearPlane) + (_farPlane + _nearPlane)) / T (2); } else { T farTimesNear = 2 * _farPlane * _nearPlane; T farMinusNear = Zp * (_farPlane - _nearPlane) - _farPlane - _nearPlane; if (abs (farMinusNear) < 1 && abs (farTimesNear) > std::numeric_limits::max() * abs (farMinusNear)) { throw std::domain_error ("Frustum::normalizedZToDepth cannot be computed: " "near and far clipping planes of the viewing frustum " "may be too close to each other"); } return farTimesNear / farMinusNear; } } template IMATH_CONSTEXPR14 T Frustum::normalizedZToDepth (T zval) const IMATH_NOEXCEPT { T Zp = zval * T (2) - T (1); if (_orthographic) { return -(Zp * (_farPlane - _nearPlane) + (_farPlane + _nearPlane)) / T (2); } else { T farTimesNear = 2 * _farPlane * _nearPlane; T farMinusNear = Zp * (_farPlane - _nearPlane) - _farPlane - _nearPlane; return farTimesNear / farMinusNear; } } template IMATH_CONSTEXPR14 long Frustum::DepthToZExc (T depth, long zmin, long zmax) const { long zdiff = zmax - zmin; T farMinusNear = _farPlane - _nearPlane; if (_orthographic) { T farPlusNear = T (2) * depth + _farPlane + _nearPlane; if (abs (farMinusNear) < T (1) && abs (farPlusNear) > std::numeric_limits::max() * abs (farMinusNear)) { throw std::domain_error ("Bad viewing frustum: " "near and far clipping planes " "are too close to each other"); } T Zp = -farPlusNear / farMinusNear; return long (0.5 * (Zp + 1) * zdiff) + zmin; } else { // Perspective T farTimesNear = T (2) * _farPlane * _nearPlane; if (abs (depth) < T (1) && abs (farTimesNear) > std::numeric_limits::max() * abs (depth)) { throw std::domain_error ("Bad call to DepthToZ function: " "value of `depth' is too small"); } T farPlusNear = farTimesNear / depth + _farPlane + _nearPlane; if (abs (farMinusNear) < T (1) && abs (farPlusNear) > std::numeric_limits::max() * abs (farMinusNear)) { throw std::domain_error ("Bad viewing frustum: " "near and far clipping planes " "are too close to each other"); } T Zp = farPlusNear / farMinusNear; return long (0.5 * (Zp + 1) * zdiff) + zmin; } } template IMATH_CONSTEXPR14 long Frustum::DepthToZ (T depth, long zmin, long zmax) const IMATH_NOEXCEPT { long zdiff = zmax - zmin; T farMinusNear = _farPlane - _nearPlane; if (_orthographic) { T farPlusNear = T (2) * depth + _farPlane + _nearPlane; T Zp = -farPlusNear / farMinusNear; return long (0.5 * (Zp + 1) * zdiff) + zmin; } else { // Perspective T farTimesNear = T (2) * _farPlane * _nearPlane; T farPlusNear = farTimesNear / depth + _farPlane + _nearPlane; T Zp = farPlusNear / farMinusNear; return long (0.5 * (Zp + 1) * zdiff) + zmin; } } template IMATH_CONSTEXPR14 T Frustum::screenRadiusExc (const Vec3& p, T radius) const { // Derivation: // Consider X-Z plane. // X coord of projection of p = xp = p.x * (-_nearPlane / p.z) // Let q be p + (radius, 0, 0). // X coord of projection of q = xq = (p.x - radius) * (-_nearPlane / p.z) // X coord of projection of segment from p to q = r = xp - xq // = radius * (-_nearPlane / p.z) // A similar analysis holds in the Y-Z plane. // So r is the quantity we want to return. if (abs (p.z) > T (1) || abs (-_nearPlane) < std::numeric_limits::max() * abs (p.z)) { return radius * (-_nearPlane / p.z); } else { throw std::domain_error ("Bad call to Frustum::screenRadius: " "magnitude of `p' is too small"); } return radius * (-_nearPlane / p.z); } template IMATH_CONSTEXPR14 T Frustum::screenRadius (const Vec3& p, T radius) const IMATH_NOEXCEPT { // Derivation: // Consider X-Z plane. // X coord of projection of p = xp = p.x * (-_nearPlane / p.z) // Let q be p + (radius, 0, 0). // X coord of projection of q = xq = (p.x - radius) * (-_nearPlane / p.z) // X coord of projection of segment from p to q = r = xp - xq // = radius * (-_nearPlane / p.z) // A similar analysis holds in the Y-Z plane. // So r is the quantity we want to return. return radius * (-_nearPlane / p.z); } template IMATH_CONSTEXPR14 T Frustum::worldRadiusExc (const Vec3& p, T radius) const { if (abs (-_nearPlane) > T (1) || abs (p.z) < std::numeric_limits::max() * abs (-_nearPlane)) { return radius * (p.z / -_nearPlane); } else { throw std::domain_error ("Bad viewing frustum: " "near clipping plane is too close to zero"); } } template IMATH_CONSTEXPR14 T Frustum::worldRadius (const Vec3& p, T radius) const IMATH_NOEXCEPT { return radius * (p.z / -_nearPlane); } template void Frustum::planes (Plane3 p[6]) const IMATH_NOEXCEPT { // // Plane order: Top, Right, Bottom, Left, Near, Far. // Normals point outwards. // if (!_orthographic) { Vec3 a (_left, _bottom, -_nearPlane); Vec3 b (_left, _top, -_nearPlane); Vec3 c (_right, _top, -_nearPlane); Vec3 d (_right, _bottom, -_nearPlane); Vec3 o (0, 0, 0); p[0].set (o, c, b); p[1].set (o, d, c); p[2].set (o, a, d); p[3].set (o, b, a); } else { p[0].set (Vec3 (0, 1, 0), _top); p[1].set (Vec3 (1, 0, 0), _right); p[2].set (Vec3 (0, -1, 0), -_bottom); p[3].set (Vec3 (-1, 0, 0), -_left); } p[4].set (Vec3 (0, 0, 1), -_nearPlane); p[5].set (Vec3 (0, 0, -1), _farPlane); } template void Frustum::planes (Plane3 p[6], const Matrix44& M) const IMATH_NOEXCEPT { // // Plane order: Top, Right, Bottom, Left, Near, Far. // Normals point outwards. // Vec3 a = Vec3 (_left, _bottom, -_nearPlane) * M; Vec3 b = Vec3 (_left, _top, -_nearPlane) * M; Vec3 c = Vec3 (_right, _top, -_nearPlane) * M; Vec3 d = Vec3 (_right, _bottom, -_nearPlane) * M; if (!_orthographic) { double s = _farPlane / double (_nearPlane); T farLeft = (T) (s * _left); T farRight = (T) (s * _right); T farTop = (T) (s * _top); T farBottom = (T) (s * _bottom); Vec3 e = Vec3 (farLeft, farBottom, -_farPlane) * M; Vec3 f = Vec3 (farLeft, farTop, -_farPlane) * M; Vec3 g = Vec3 (farRight, farTop, -_farPlane) * M; Vec3 o = Vec3 (0, 0, 0) * M; p[0].set (o, c, b); p[1].set (o, d, c); p[2].set (o, a, d); p[3].set (o, b, a); p[4].set (a, d, c); p[5].set (e, f, g); } else { Vec3 e = Vec3 (_left, _bottom, -_farPlane) * M; Vec3 f = Vec3 (_left, _top, -_farPlane) * M; Vec3 g = Vec3 (_right, _top, -_farPlane) * M; Vec3 h = Vec3 (_right, _bottom, -_farPlane) * M; p[0].set (c, g, f); p[1].set (d, h, g); p[2].set (a, e, h); p[3].set (b, f, e); p[4].set (a, d, c); p[5].set (e, f, g); } } /// Frustum of type float typedef Frustum Frustumf; /// Frustum of type double typedef Frustum Frustumd; IMATH_INTERNAL_NAMESPACE_HEADER_EXIT #if defined _WIN32 || defined _WIN64 # ifdef _redef_near # define near # endif # ifdef _redef_far # define far # endif #endif #endif // INCLUDED_IMATHFRUSTUM_H