1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_GF_FRUSTUM_H
25 #define PXR_BASE_GF_FRUSTUM_H
26 
27 /// \file gf/frustum.h
28 /// \ingroup group_gf_BasicGeometry
29 
30 #include "pxr/pxr.h"
31 #include "pxr/base/gf/bbox3d.h"
32 #include "pxr/base/gf/matrix4d.h"
33 #include "pxr/base/gf/plane.h"
34 #include "pxr/base/gf/ray.h"
35 #include "pxr/base/gf/range1d.h"
36 #include "pxr/base/gf/range2d.h"
37 #include "pxr/base/gf/rotation.h"
38 #include "pxr/base/gf/vec2d.h"
39 #include "pxr/base/gf/vec3d.h"
40 #include "pxr/base/gf/api.h"
41 
42 #include <boost/functional/hash.hpp>
43 
44 #include <array>
45 #include <atomic>
46 #include <iosfwd>
47 #include <vector>
48 
49 PXR_NAMESPACE_OPEN_SCOPE
50 
51 /// \class GfFrustum
52 /// \ingroup group_gf_BasicGeometry
53 ///
54 /// Basic type: View frustum.
55 ///
56 /// This class represents a viewing frustum in three dimensional eye space. It
57 /// may represent either a parallel (orthographic) or perspective projection.
58 /// One can think of the frustum as being defined by 6 boundary planes.
59 ///
60 /// The frustum is specified using these parameters:
61 ///  \li The \em position of the viewpoint.
62 ///  \li The \em rotation applied to the default view frame, which is
63 ///      looking along the -z axis with the +y axis as the "up"
64 ///      direction.
65 ///  \li The 2D \em window on the reference plane that defines the left,
66 ///      right, top, and bottom planes of the viewing frustum, as
67 ///      described below.
68 ///  \li The distances to the \em near and \em far planes.
69 ///  \li The \em projection \em type
70 ///  \li The view distance.
71 ///
72 /// The window and near/far parameters combine to define the view frustum as
73 /// follows. Transform the -z axis and the +y axis by the frustum rotation to
74 /// get the world-space \em view \em direction and \em up \em direction. Now
75 /// consider the \em reference \em plane that is perpendicular to the view
76 /// direction, a distance of referencePlaneDepth from the viewpoint, and whose
77 /// y axis corresponds to the up direction.  The window rectangle is specified
78 /// in a 2D coordinate system embedded in this plane. The origin of the
79 /// coordinate system is the point at which the view direction vector
80 /// intersects the plane. Therefore, the point (0,1) in this plane is found by
81 /// moving 1 unit along the up direction vector in this plane. The vector from
82 /// the viewpoint to the resulting point will form a 45-degree angle with the
83 /// view direction.
84 ///
85 /// The view distance is only useful for interactive applications. It can be
86 /// used to compute a look at point which is useful when rotating around an
87 /// object of interest.
88 ///
89 class GfFrustum {
90   public:
91     /// This enum is used to determine the type of projection represented by a
92     /// frustum.
93     enum ProjectionType {
94         Orthographic,                   ///< Orthographic projection
95         Perspective,                    ///< Perspective projection
96     };
97 
98     /// This constructor creates an instance with default viewing parameters:
99     /// \li The position is the origin.
100     /// \li The rotation is the identity rotation. (The view is along
101     ///     the -z axis, with the +y axis as "up").
102     /// \li The window is -1 to +1 in both dimensions.
103     /// \li The near/far interval is (1, 10).
104     /// \li The view distance is 5.0.
105     /// \li The projection type is \c GfFrustum::Perspective.
106     GF_API GfFrustum();
107 
108     /// Copy constructor.
GfFrustum(GfFrustum const & o)109     GfFrustum(GfFrustum const &o)
110         : _position(o._position)
111         , _rotation(o._rotation)
112         , _window(o._window)
113         , _nearFar(o._nearFar)
114         , _viewDistance(o._viewDistance)
115         , _projectionType(o._projectionType)
116         , _planes(nullptr) {
117         if (auto *planes = o._planes.load()) {
118             _planes = new std::array<GfPlane, 6>(*planes);
119         }
120     }
121 
122     /// Move constructor.
GfFrustum(GfFrustum && o)123     GfFrustum(GfFrustum &&o) noexcept
124         : _position(o._position)
125         , _rotation(o._rotation)
126         , _window(o._window)
127         , _nearFar(o._nearFar)
128         , _viewDistance(o._viewDistance)
129         , _projectionType(o._projectionType)
130         , _planes(nullptr) {
131         if (auto *planes =
132             o._planes.exchange(nullptr, std::memory_order_relaxed)) {
133             _planes = planes;
134         }
135     }
136 
137     /// This constructor creates an instance with the given viewing
138     /// parameters.
139     GF_API GfFrustum(const GfVec3d &position, const GfRotation &rotation,
140               const GfRange2d &window, const GfRange1d &nearFar,
141               GfFrustum::ProjectionType projectionType,
142               double viewDistance = 5.0);
143 
144     /// This constructor creates an instance from a camera matrix (always of a
145     /// y-Up camera, also see SetPositionAndRotationFromMatrix) and the given
146     /// viewing parameters.
147     GF_API GfFrustum(const GfMatrix4d &camToWorldXf,
148               const GfRange2d &window, const GfRange1d &nearFar,
149               GfFrustum::ProjectionType projectionType,
150               double viewDistance = 5.0);
151 
152     /// Copy assignment.
153     GfFrustum &operator=(GfFrustum const &o) noexcept {
154         if (this == &o) {
155             return *this;
156         }
157         _position = o._position;
158         _rotation = o._rotation;
159         _window = o._window;
160         _nearFar = o._nearFar;
161         _viewDistance = o._viewDistance;
162         _projectionType = o._projectionType;
163         delete _planes.load(std::memory_order_relaxed);
164         if (auto *planes = o._planes.load(std::memory_order_relaxed)) {
165             _planes.store(new std::array<GfPlane, 6>(*planes),
166                           std::memory_order_relaxed);
167         }
168         else {
169             _planes.store(nullptr, std::memory_order_relaxed);
170         }
171         return *this;
172     }
173 
174     /// Move assignment.
175     GfFrustum &operator=(GfFrustum &&o) noexcept {
176         if (this == &o) {
177             return *this;
178         }
179         _position = o._position;
180         _rotation = o._rotation;
181         _window = o._window;
182         _nearFar = o._nearFar;
183         _viewDistance = o._viewDistance;
184         _projectionType = o._projectionType;
185         delete _planes.load(std::memory_order_relaxed);
186         _planes.store(o._planes.load(std::memory_order_relaxed),
187                       std::memory_order_relaxed);
188         o._planes.store(nullptr, std::memory_order_relaxed);
189         return *this;
190     }
191 
hash_value(const GfFrustum & f)192     friend inline size_t hash_value(const GfFrustum &f) {
193         size_t h = 0;
194         boost::hash_combine(h, f._position);
195         boost::hash_combine(h, f._rotation);
196         boost::hash_combine(h, f._window);
197         boost::hash_combine(h, f._nearFar);
198         boost::hash_combine(h, f._viewDistance);
199         boost::hash_combine(h, f._projectionType);
200         return h;
201     }
202 
203     // Equality operator. true iff all parts match.
204     bool operator ==(const GfFrustum& f) const {
205         if (_position       != f._position)        return false;
206         if (_rotation       != f._rotation)        return false;
207         if (_window         != f._window)          return false;
208         if (_nearFar        != f._nearFar)         return false;
209         if (_viewDistance   != f._viewDistance)    return false;
210         if (_projectionType != f._projectionType)  return false;
211 
212         return true;
213     }
214 
215     // Inequality operator. true iff not equality.
216     bool operator !=(const GfFrustum& f) const {
217         return !(*this == f);
218     }
219 
220     /// Destructor.
221     GF_API ~GfFrustum();
222 
223     /// \name Value setting and access
224     /// The methods in this group set and access the values that are used to
225     /// define a frustum.
226     ///@{
227 
228     /// Sets the position of the frustum in world space.
SetPosition(const GfVec3d & position)229     void                SetPosition(const GfVec3d &position) {
230         _position = position;
231         _DirtyFrustumPlanes();
232     }
233 
234     /// Returns the position of the frustum in world space.
GetPosition()235     const GfVec3d &     GetPosition() const {
236         return _position;
237     }
238 
239     /// Sets the orientation of the frustum in world space as a rotation to
240     /// apply to the default frame: looking along the -z axis with the +y axis
241     /// as "up".
SetRotation(const GfRotation & rotation)242     void                SetRotation(const GfRotation &rotation) {
243         _rotation = rotation;
244         _DirtyFrustumPlanes();
245     }
246 
247     /// Returns the orientation of the frustum in world space as a rotation to
248     /// apply to the -z axis.
GetRotation()249     const GfRotation &  GetRotation() const {
250         return _rotation;
251     }
252 
253     /// Sets the position and rotation of the frustum from a camera matrix
254     /// (always from a y-Up camera). The resulting frustum's transform will
255     /// always represent a right-handed and orthonormal coordinate sytem
256     /// (scale, shear, and projection are removed from the given \p
257     /// camToWorldXf).
258     GF_API void SetPositionAndRotationFromMatrix(const GfMatrix4d &camToWorldXf);
259 
260     /// Sets the window rectangle in the reference plane that defines the
261     /// left, right, top, and bottom planes of the frustum.
SetWindow(const GfRange2d & window)262     void                SetWindow(const GfRange2d &window)  {
263         _window = window;
264         _DirtyFrustumPlanes();
265     }
266 
267     /// Returns the window rectangle in the reference plane.
GetWindow()268     const GfRange2d &   GetWindow() const {
269         return _window;
270     }
271 
272     /// Returns the depth of the reference plane.
GetReferencePlaneDepth()273     static double GetReferencePlaneDepth() {
274         return 1.0;
275     }
276 
277     /// Sets the near/far interval.
SetNearFar(const GfRange1d & nearFar)278     void                SetNearFar(const GfRange1d &nearFar) {
279         _nearFar = nearFar;
280         _DirtyFrustumPlanes();
281     }
282 
283     /// Returns the near/far interval.
GetNearFar()284     const GfRange1d &   GetNearFar() const {
285         return _nearFar;
286     }
287 
288     /// Sets the view distance.
SetViewDistance(double viewDistance)289     void                SetViewDistance(double viewDistance) {
290         _viewDistance = viewDistance;
291     }
292 
293     /// Returns the view distance.
GetViewDistance()294     double              GetViewDistance() const {
295         return _viewDistance;
296     }
297 
298     /// Sets the projection type.
SetProjectionType(GfFrustum::ProjectionType projectionType)299     void        SetProjectionType(GfFrustum::ProjectionType projectionType) {
300         _projectionType = projectionType;
301         _DirtyFrustumPlanes();
302     }
303 
304     /// Returns the projection type.
GetProjectionType()305     GfFrustum::ProjectionType   GetProjectionType() const {
306         return _projectionType;
307     }
308 
309     ///@}
310 
311     /// \name Convenience methods
312     ///
313     /// The methods in this group allow the frustum's data to be accessed and
314     /// modified in terms of different representations that may be more
315     /// convenient for certain applications.
316     ///
317     ///@{
318 
319     /// Sets up the frustum in a manner similar to \c gluPerspective().
320     ///
321     /// It sets the projection type to \c GfFrustum::Perspective and sets the
322     /// window specification so that the resulting symmetric frustum encloses
323     /// an angle of \p fieldOfViewHeight degrees in the vertical direction,
324     /// with \p aspectRatio used to figure the angle in the horizontal
325     /// direction. The near and far distances are specified as well. The
326     /// window coordinates are computed as:
327     /// \code
328     ///     top    = tan(fieldOfViewHeight / 2)
329     ///     bottom = -top
330     ///     right  = top * aspectRatio
331     ///     left   = -right
332     ///     near   = nearDistance
333     ///     far    = farDistance
334     /// \endcode
335     ///
336     GF_API void         SetPerspective(double fieldOfViewHeight,
337                                        double aspectRatio,
338                                        double nearDistance, double farDistance);
339 
340     /// Sets up the frustum in a manner similar to gluPerspective().
341     ///
342     /// It sets the projection type to \c GfFrustum::Perspective and
343     /// sets the window specification so that:
344     ///
345     /// If \a isFovVertical is true, the resulting symmetric frustum encloses
346     /// an angle of \p fieldOfView degrees in the vertical direction, with \p
347     /// aspectRatio used to figure the angle in the horizontal direction.
348     ///
349     /// If \a isFovVertical is false, the resulting symmetric frustum encloses
350     /// an angle of \p fieldOfView degrees in the horizontal direction, with
351     /// \p aspectRatio used to figure the angle in the vertical direction.
352     ///
353     /// The near and far distances are specified as well. The window
354     /// coordinates are computed as follows:
355     ///
356     /// \li if isFovVertical:
357     ///     \li top    = tan(fieldOfView / 2)
358     ///     \li right  = top * aspectRatio
359     /// \li if NOT isFovVertical:
360     ///     \li right    = tan(fieldOfView / 2)
361     ///     \li top  = right / aspectRation
362     /// \li bottom = -top
363     /// \li left   = -right
364     /// \li near   = nearDistance
365     /// \li far    = farDistance
366     ///
367     GF_API void         SetPerspective(double fieldOfView,
368                                        bool   isFovVertical,
369                                        double aspectRatio,
370                                        double nearDistance, double farDistance);
371 
372     /// Returns the current frustum in the format used by \c SetPerspective().
373     /// If the current frustum is not a perspective projection, this returns
374     /// \c false and leaves the parameters untouched.
375     GF_API bool         GetPerspective(double *fieldOfViewHeight,
376                                        double *aspectRatio,
377                                        double *nearDistance,
378                                        double *farDistance) const;
379 
380     /// Returns the current frustum in the format used by \c SetPerspective().
381     /// If the current frustum is not a perspective projection, this returns
382     /// \c false and leaves the parameters untouched.
383     GF_API bool         GetPerspective(bool   isFovVertical,
384                                        double *fieldOfView,
385                                        double *aspectRatio,
386                                        double *nearDistance,
387                                        double *farDistance) const;
388 
389     /// Returns the horizontal or vertical fov of the frustum. The fov of the
390     /// frustum is not necessarily the same value as displayed in the viewer.
391     /// The displayed fov is a function of the focal length or FOV avar. The
392     /// frustum's fov may be different due to things like lens breathing.
393     ///
394     /// If the frustum is not of type \c GfFrustum::Perspective, the returned
395     /// FOV will be 0.0.
396     ///
397     /// \note The default value for \c isFovVertical is false so calling \c
398     /// GetFOV without an argument will return the horizontal field of view
399     /// which is compatible with menv2x's old GfFrustum::GetFOV routine.
400     GF_API double       GetFOV(bool isFovVertical = false);
401 
402     /// Sets up the frustum in a manner similar to \c glOrtho().
403     ///
404     /// Sets the projection to \c GfFrustum::Orthographic and sets the window
405     /// and near/far specifications based on the given values.
406     GF_API
407     void                SetOrthographic(double left, double right,
408                                         double bottom, double top,
409                                         double nearPlane, double farPlane);
410 
411     /// Returns the current frustum in the format used by \c
412     /// SetOrthographic(). If the current frustum is not an orthographic
413     /// projection, this returns \c false and leaves the parameters untouched.
414     GF_API bool         GetOrthographic(double *left, double *right,
415                                         double *bottom, double *top,
416                                         double *nearPlane, double *farPlane)
417                                         const;
418 
419     /// Modifies the frustum to tightly enclose a sphere with the given center
420     /// and radius, using the current view direction. The planes of the
421     /// frustum are adjusted as necessary. The given amount of slack is added
422     /// to the sphere's radius is used around the sphere to avoid boundary
423     /// problems.
424     GF_API void         FitToSphere(const GfVec3d &center,
425                                     double radius,
426                                     double slack = 0.0);
427 
428     /// Transforms the frustum by the given matrix.
429     ///
430     /// The transformation matrix is applied as follows: the position and the
431     /// direction vector are transformed with the given matrix. Then the
432     /// length of the new direction vector is used to rescale the near and far
433     /// plane and the view distance. Finally, the points that define the
434     /// reference plane are transformed by the matrix. This method assures
435     /// that the frustum will not be sheared or perspective-projected.
436     ///
437     /// \note Note that this definition means that the transformed frustum
438     /// does not preserve scales very well. Do \em not use this function to
439     /// transform a frustum that is to be used for precise operations such as
440     /// intersection testing.
441     GF_API GfFrustum&   Transform(const GfMatrix4d &matrix);
442 
443     /// Returns the normalized world-space view direction vector, which is
444     /// computed by rotating the -z axis by the frustum's rotation.
445     GF_API GfVec3d      ComputeViewDirection() const;
446 
447     /// Returns the normalized world-space up vector, which is computed by
448     /// rotating the y axis by the frustum's rotation.
449     GF_API GfVec3d      ComputeUpVector() const;
450 
451     /// Computes the view frame defined by this frustum. The frame consists of
452     /// the view direction, up vector and side vector, as shown in this
453     /// diagram.
454     ///
455     /// \code
456     ///            up
457     ///            ^   ^
458     ///            |  /
459     ///            | / view
460     ///            |/
461     ///            +- - - - > side
462     /// \endcode
463     ///
464     GF_API void         ComputeViewFrame(GfVec3d *side,
465                                          GfVec3d *up,
466                                          GfVec3d *view) const;
467 
468     /// Computes and returns the world-space look-at point from the eye point
469     /// (position), view direction (rotation), and view distance.
470     GF_API GfVec3d      ComputeLookAtPoint() const;
471 
472     /// Returns a matrix that represents the viewing transformation for this
473     /// frustum.  That is, it returns the matrix that converts points from
474     /// world space to eye (frustum) space.
475     GF_API GfMatrix4d   ComputeViewMatrix() const;
476 
477     /// Returns a matrix that represents the inverse viewing transformation
478     /// for this frustum.  That is, it returns the matrix that converts points
479     /// from eye (frustum) space to world space.
480     GF_API GfMatrix4d   ComputeViewInverse() const;
481 
482     /// Returns a GL-style projection matrix corresponding to the frustum's
483     /// projection.
484     GF_API GfMatrix4d   ComputeProjectionMatrix() const;
485 
486     /// Returns the aspect ratio of the frustum, defined as the width of the
487     /// window divided by the height. If the height is zero or negative, this
488     /// returns 0.
489     GF_API double       ComputeAspectRatio() const;
490 
491     /// Returns the world-space corners of the frustum as a vector of 8
492     /// points, ordered as:
493     /// \li Left bottom near
494     /// \li Right bottom near
495     /// \li Left top near
496     /// \li Right top near
497     /// \li Left bottom far
498     /// \li Right bottom far
499     /// \li Left top far
500     /// \li Right top far
501     GF_API
502     std::vector<GfVec3d> ComputeCorners() const;
503 
504     /// Returns the world-space corners of the intersection of the frustum
505     /// with a plane parallel to the near/far plane at distance d from the
506     /// apex, ordered as:
507     /// \li Left bottom
508     /// \li Right bottom
509     /// \li Left top
510     /// \li Right top
511     /// In particular, it gives the partial result of ComputeCorners when given
512     /// near or far distance.
513     GF_API
514     std::vector<GfVec3d> ComputeCornersAtDistance(double d) const;
515 
516     /// Returns a frustum that is a narrowed-down version of this frustum,
517     /// such that the frustum rectangle on the near plane encloses \p point
518     /// with at most \p halfSize[0] distance on the left and right and at most
519     /// \p halfSize[1] distance on the top and bottom. (If \p point is closer
520     /// than the half size to a side of the frustum, that side is left alone.
521     /// The point and sizes are in normalized 2D coordinates; they range from
522     /// (-1, -1) at the lower left corner of the near-plane window rectangle
523     /// to (1,1) at the upper right corner.
524     ///
525     /// \p point is a 2d point expressed as a normalized window position.
526     ///
527     /// This method is useful for computing a volume to use for interactive
528     /// picking.
529     GF_API GfFrustum    ComputeNarrowedFrustum(const GfVec2d &point,
530                                                const GfVec2d &halfSize) const;
531 
532     /// Returns a frustum that is a narrowed-down version of this frustum,
533     /// such that the frustum rectangle on the near plane encloses \p point
534     /// with at most \p halfSize[0] distance on the left and right and at most
535     /// \p halfSize[1] distance on the top and bottom. (If \p point is closer
536     /// than the half size to a side of the frustum, that side is left alone.
537     /// The point and sizes are in normalized 2D coordinates; they range from
538     /// (-1, -1) at the lower left corner of the near-plane window rectangle
539     /// to (1,1) at the upper right corner.
540     ///
541     /// \p point is a 3d point expressed in world coordinates
542     ///
543     /// This method is useful for computing a volume to use for interactive
544     /// picking.
545     GF_API GfFrustum    ComputeNarrowedFrustum(const GfVec3d &worldPoint,
546                                                const GfVec2d &halfSize) const;
547 
548     /// Builds and returns a \c GfRay that starts at the viewpoint and extends
549     /// through the given \a windowPos given in normalized coords (-1 to +1 in
550     /// both dimensions) window position.
551     ///
552     /// Contrasted with ComputePickRay(), this method returns a ray whose
553     /// origin is the eyepoint, while that method returns a ray whose origin
554     /// is on the near plane.
555     GF_API GfRay        ComputeRay(const GfVec2d &windowPos) const;
556 
557     /// Builds and returns a \c GfRay that connects the viewpoint to the given
558     /// 3d point in worldspace.
559     ///
560     /// Contrasted with ComputePickRay(), this method returns a ray whose
561     /// origin is the eyepoint, while that method returns a ray whose origin
562     /// is on the near plane.
563     GF_API GfRay        ComputeRay(const GfVec3d &worldSpacePos) const;
564 
565     /// Builds and returns a \c GfRay that can be used for picking at the
566     /// given normalized (-1 to +1 in both dimensions) window position.
567     ///
568     /// Contrasted with ComputeRay(), that method returns a ray whose origin
569     /// is the eyepoint, while this method returns a ray whose origin is on
570     /// the near plane.
571     GF_API GfRay        ComputePickRay(const GfVec2d &windowPos) const;
572 
573     /// Builds and returns a \c GfRay that can be used for picking that
574     /// connects the viewpoint to the given 3d point in worldspace.
575     GF_API GfRay       ComputePickRay(const GfVec3d &worldSpacePos) const;
576 
577     ///@}
578 
579     /// \name Intersection methods
580     ///
581     /// The methods in this group implement intersection operations
582     /// between this frustum and a given primitive.
583     ///
584     ///@{
585 
586     /// Returns true if the given axis-aligned bbox is inside or intersecting
587     /// the frustum. Otherwise, it returns false. Useful when doing picking or
588     /// frustum culling.
589     GF_API bool         Intersects(const GfBBox3d &bbox) const;
590 
591     /// Returns true if the given point is inside or intersecting the frustum.
592     /// Otherwise, it returns false.
593     GF_API bool         Intersects(const GfVec3d &point) const;
594 
595     /// Returns \c true if the line segment formed by the given points is
596     /// inside or intersecting the frustum.  Otherwise, it returns false.
597     GF_API bool         Intersects(const GfVec3d &p0,
598                                    const GfVec3d &p1) const;
599 
600     /// Returns \c true if the triangle formed by the given points is inside
601     /// or intersecting the frustum.  Otherwise, it returns false.
602     GF_API bool         Intersects(const GfVec3d &p0,
603                                    const GfVec3d &p1,
604                                    const GfVec3d &p2) const;
605 
606     /// Returns \c true if the bbox volume intersects the view volume given by
607     /// the view-projection matrix, erring on the side of false positives for
608     /// efficiency.
609     ///
610     /// This method is intended for cases where a GfFrustum is not available
611     /// or when the view-projection matrix yields a view volume that is not
612     /// expressable as a GfFrustum.
613     ///
614     /// Because it errs on the side of false positives, it is suitable for
615     /// early-out tests such as draw or intersection culling.
616     ///
617     GF_API static bool  IntersectsViewVolume(const GfBBox3d &bbox,
618                                              const GfMatrix4d &vpMat);
619 
620     ///@}
621 
622   private:
623     // Dirty the result of _CalculateFrustumPlanes.
624       GF_API void _DirtyFrustumPlanes();
625 
626     // Calculates cached frustum planes used for intersection tests.
627       GF_API void       _CalculateFrustumPlanes() const;
628 
629     // Builds and returns a \c GfRay that can be used for picking. Given an
630     // eye position and direction in camera space, offsets the ray to emanate
631     // from the near plane, then transforms into worldspace
632       GF_API GfRay      _ComputePickRayOffsetToNearPlane(
633                                     const GfVec3d &camSpaceFrom,
634                                     const GfVec3d &camSpaceDir) const;
635 
636     // Returns a frustum that is a narrowed-down version of this frustum, such
637     // that the frustum rectangle on the near plane encloses \p point with at
638     // most \p halfSize[0] distance on the left and right and at most \p
639     // halfSize[1] distance on the top and bottom. (If \p point is closer than
640     // the half size to a side of the frustum, that side is left alone. The
641     // point and sizes are in normalized 2D coordinates; they range from (-1,
642     // -1) at the lower left corner of the near-plane window rectangle to
643     // (1,1) at the upper right corner.
644     //
645     // \p windowPoint is expressed in window coordinates
646     //
647     // This method is useful for computing a volume to use for interactive
648     // picking.
649     GfFrustum           _ComputeNarrowedFrustumSub(const GfVec2d windowPoint,
650                                     const GfVec2d &halfSize) const;
651 
652     bool _SegmentIntersects(GfVec3d const &p0, uint32_t p0Mask,
653                             GfVec3d const &p1, uint32_t p1Mask) const;
654 
655     // Position of the frustum in world space.
656     GfVec3d                     _position;
657 
658     // Orientation of the frustum in world space as a rotation to apply to the
659     // -z axis.
660     GfRotation                  _rotation;
661 
662     // Window rectangle in the image plane.
663     GfRange2d                   _window;
664 
665     // Near/far interval.
666     GfRange1d                   _nearFar;
667 
668     // View distance.
669     double                      _viewDistance;
670 
671     // Projection type.
672     ProjectionType              _projectionType;
673 
674     // Cached planes.
675     // If null, the planes have not been calculated.
676     mutable std::atomic<std::array<GfPlane, 6> *> _planes;
677 };
678 
679 /// Output a GfFrustum using the format [(position) (rotation) [window]
680 /// [nearFar] viewDistance type]
681 ///
682 /// The "type" is "perspective", or "orthographic, depending on the
683 /// projection type of the frustum.
684 ///
685 /// \ingroup group_gf_DebuggingOutput
686 GF_API std::ostream& operator<<(std::ostream& out, const GfFrustum& f);
687 
688 PXR_NAMESPACE_CLOSE_SCOPE
689 
690 #endif // PXR_BASE_GF_FRUSTUM_H
691