1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SbDPViewVolume SbDPViewVolume.h Inventor/SbDPViewVolume.h
35   \brief The SbDPViewVolume class is a double precision viewing volume in 3D space.
36 
37   \ingroup base
38 
39   This class contains the necessary information for storing a view
40   volume.  It has methods for projection of primitives from or into
41   the 3D volume, doing camera transforms, view volume transforms etc.
42 
43   \COIN_CLASS_EXTENSION
44 
45   \sa SbViewportRegion
46   \since Coin 2.0
47 */
48 
49 #include <Inventor/SbDPViewVolume.h>
50 #include <Inventor/SbViewVolume.h>
51 #include <Inventor/SbDPRotation.h>
52 #include <Inventor/SbDPLine.h>
53 #include <Inventor/SbLine.h>
54 #include <Inventor/SbDPMatrix.h>
55 #include <Inventor/SbPlane.h>
56 #include <Inventor/SbBox2f.h>
57 #include <Inventor/SbBox3f.h>
58 #include <Inventor/SbVec2d.h>
59 #include <Inventor/SbVec3d.h>
60 #include <Inventor/SbDPPlane.h>
61 #if COIN_DEBUG
62 #include <Inventor/errors/SoDebugError.h>
63 #endif // COIN_DEBUG
64 
65 #include <coindefs.h> // COIN_OBSOLETED()
66 #include <cassert>
67 
68 /*!
69   \enum SbDPViewVolume::ProjectionType
70 
71   An SbDPViewVolume instance can represent either an orthogonal projection
72   volume or a perspective projection volume.
73 
74   \sa ortho(), perspective(), getProjectionType().
75 */
76 
77 /*!
78   \var SbDPViewVolume::ProjectionType SbDPViewVolume::ORTHOGRAPHIC
79   Orthographic projection.
80 */
81 
82 /*!
83   \var SbDPViewVolume::ProjectionType SbDPViewVolume::PERSPECTIVE
84   Perspective projection.
85 */
86 
87 /*!
88   \var SbDPViewVolume::ProjectionType SbDPViewVolume::type
89   \COININTERNAL
90 */
91 
92 /*!
93   \var SbVec3d SbDPViewVolume::projPoint
94   \COININTERNAL
95 */
96 
97 /*!
98   \var SbVec3d SbDPViewVolume::projDir
99   \COININTERNAL
100 */
101 
102 /*!
103   \var double SbDPViewVolume::nearDist
104   \COININTERNAL
105 */
106 
107 /*!
108   \var double SbDPViewVolume::nearToFar
109   \COININTERNAL
110 */
111 
112 /*!
113   \var SbVec3d SbDPViewVolume::llf
114   \COININTERNAL
115 */
116 
117 /*!
118   \var SbVec3d SbDPViewVolume::lrf
119   \COININTERNAL
120 */
121 
122 /*!
123   \var SbVec3d SbDPViewVolume::ulf
124   \COININTERNAL
125 */
126 
127 //
128 // some convenience function for converting between single precision
129 // and double precision classes.
130 //
131 static SbVec3f
dp_to_sbvec3f(const SbVec3d & v)132 dp_to_sbvec3f(const SbVec3d & v)
133 {
134   return SbVec3f(static_cast<float>(v[0]), static_cast<float>(v[1]), static_cast<float>(v[2]));
135 }
136 
137 static SbVec3d
dp_to_sbvec3d(const SbVec3f & v)138 dp_to_sbvec3d(const SbVec3f & v)
139 {
140   return SbVec3d(static_cast<double>(v[0]), static_cast<double>(v[1]), static_cast<double>(v[2]));
141 }
142 
143 /*!
144   Constructor. Note that the SbDPViewVolume instance will be uninitialized
145   until you explicitly call \a ortho() or \a perspective().
146 
147   \sa ortho(), perspective().
148  */
SbDPViewVolume(void)149 SbDPViewVolume::SbDPViewVolume(void)
150 {
151 }
152 
153 /*!
154   Destructor.
155  */
~SbDPViewVolume(void)156 SbDPViewVolume::~SbDPViewVolume(void)
157 {
158 }
159 
160 // Perspective projection matrix. From the "OpenGL Programming Guide,
161 // release 1", Appendix G (but with row-major mode).
162 static SbDPMatrix
get_perspective_projection(const double rightminusleft,const double rightplusleft,const double topminusbottom,const double topplusbottom,const double nearval,const double farval)163 get_perspective_projection(const double rightminusleft, const double rightplusleft,
164                            const double topminusbottom, const double topplusbottom,
165                            const double nearval, const double farval)
166 {
167 #if COIN_DEBUG
168   if (nearval * farval <= 0.0) {
169     SoDebugError::postWarning("SbDPViewVolume::get_perspective_projection",
170                               "Projection frustrum crosses zero. Rendering is unpredicatable.");
171   }
172 #endif // COIN_DEBUG
173   SbDPMatrix proj;
174 
175   proj[0][0] = 2.0*nearval/rightminusleft;
176   proj[0][1] = 0.0;
177   proj[0][2] = 0.0;
178   proj[0][3] = 0.0;
179   proj[1][0] = 0.0;
180   proj[1][1] = 2.0*nearval/topminusbottom;
181   proj[1][2] = 0.0;
182   proj[1][3] = 0.0;
183   proj[2][0] = rightplusleft/rightminusleft;
184   proj[2][1] = topplusbottom/topminusbottom;
185   proj[2][2] = -(farval+nearval)/(farval-nearval);
186   proj[2][3] = -1.0;
187   proj[3][0] = 0.0;
188   proj[3][1] = 0.0;
189   proj[3][2] = -2.0*farval*nearval/(farval-nearval);
190   proj[3][3] = 0.0;
191 
192   // special handling for reverse perspective projection (see SoPerspectiveCamera documentation)
193   if (nearval < 0.0) {
194     // OpenGL performs clipping in homogeneous space (before computing the perspective division).
195     // i.e. instead of testing for -1 <= z/w <= +1, it checks for -w <= z <= +w. Both conditions
196     // are only equivalent if w > 0.
197     // In the reverse perspective case the projection matrix above leads to negative w values,
198     // but this can be compensated by multiplying the whole matrix by -1.
199     proj[0][0] *= -1.0;
200     proj[1][1] *= -1.0;
201     proj[2][0] *= -1.0;
202     proj[2][1] *= -1.0;
203     proj[2][2] *= -1.0;
204     proj[2][3] *= -1.0;
205     proj[3][2] *= -1.0;
206   }
207 
208   return proj;
209 }
210 
211 
212 // Orthographic projection matrix. From the "OpenGL Programming Guide,
213 // release 1", Appendix G (but with row-major mode).
214 static SbDPMatrix
get_ortho_projection(const double rightminusleft,const double rightplusleft,const double topminusbottom,const double topplusbottom,const double nearval,const double farval)215 get_ortho_projection(const double rightminusleft, const double rightplusleft,
216                      const double topminusbottom, const double topplusbottom,
217                      const double nearval, const double farval)
218 {
219   SbDPMatrix proj;
220   proj[0][0] = 2.0/rightminusleft;
221   proj[0][1] = 0.0;
222   proj[0][2] = 0.0;
223   proj[0][3] = 0.0;
224   proj[1][0] = 0.0;
225   proj[1][1] = 2.0/topminusbottom;
226   proj[1][2] = 0.0;
227   proj[1][3] = 0.0;
228   proj[2][0] = 0.0;
229   proj[2][1] = 0.0;
230   proj[2][2] = -2.0/(farval-nearval);
231   proj[2][3] = 0.0;
232   proj[3][0] = -rightplusleft/rightminusleft;
233   proj[3][1] = -topplusbottom/topminusbottom;
234   proj[3][2] = -(farval+nearval)/(farval-nearval);
235   proj[3][3] = 1.0;
236 
237   return proj;
238 
239 }
240 
241 
242 /*!
243   Returns the view volume's affine matrix and projection matrix.
244 
245   \sa getMatrix(), getCameraSpaceMatrix()
246  */
247 void
getMatrices(SbDPMatrix & affine,SbDPMatrix & proj) const248 SbDPViewVolume::getMatrices(SbDPMatrix& affine, SbDPMatrix& proj) const
249 {
250   SbVec3d upvec = this->ulf - this->llf;
251 #if COIN_DEBUG
252   if (upvec == SbVec3d(0.0, 0.0, 0.0)) {
253     SoDebugError::postWarning("SbDPViewVolume::getMatrices",
254                               "empty frustum!");
255     affine = SbDPMatrix::identity();
256     proj = SbDPMatrix::identity();
257     return;
258   }
259 #endif // COIN_DEBUG
260   SbVec3d rightvec = this->lrf - this->llf;
261 
262 #if COIN_DEBUG
263   if (rightvec == SbVec3d(0.0, 0.0, 0.0)) {
264     SoDebugError::postWarning("SbDPViewVolume::getMatrices",
265                               "empty frustum!");
266     affine = SbDPMatrix::identity();
267     proj = SbDPMatrix::identity();
268     return;
269   }
270 #endif // COIN_DEBUG
271 
272   // we test vectors above, just normalize
273   (void) upvec.normalize();
274   (void) rightvec.normalize();
275 
276   // build matrix that will transform into camera coordinate system
277   SbDPMatrix mat;
278   mat[0][0] = rightvec[0];
279   mat[0][1] = rightvec[1];
280   mat[0][2] = rightvec[2];
281   mat[0][3] = 0.0f;
282 
283   mat[1][0] = upvec[0];
284   mat[1][1] = upvec[1];
285   mat[1][2] = upvec[2];
286   mat[1][3] = 0.0f;
287 
288   mat[2][0] = -this->projDir[0];
289   mat[2][1] = -this->projDir[1];
290   mat[2][2] = -this->projDir[2];
291   mat[2][3] = 0.0f;
292 
293   mat[3][0] = this->projPoint[0];
294   mat[3][1] = this->projPoint[1];
295   mat[3][2] = this->projPoint[2];
296   mat[3][3] = 1.0f;
297 
298   // the affine matrix is the inverse of the camera coordinate system
299   affine = mat.inverse();
300 
301   // rotate frustum points back to an axis-aligned view volume to
302   // calculate parameters for the projection matrix
303   SbVec3d nlrf, nllf, nulf;
304 
305   affine.multDirMatrix(this->lrf, nlrf);
306   affine.multDirMatrix(this->llf, nllf);
307   affine.multDirMatrix(this->ulf, nulf);
308 
309   double rml = nlrf[0] - nllf[0];
310   double rpl = nlrf[0] + nllf[0];
311   double tmb = nulf[1] - nllf[1];
312   double tpb = nulf[1] + nllf[1];
313   double n = this->getNearDist();
314   double f = n + this->getDepth();
315 
316 #if COIN_DEBUG
317   if (rml <= 0.0f || tmb <= 0.0f || n >= f) {
318     SoDebugError::postWarning("SbDPViewVolume::getMatrices",
319                               "invalid frustum");
320     proj = SbDPMatrix::identity();
321     return;
322   }
323 #endif // COIN_DEBUG
324 
325 
326   if(this->type == SbDPViewVolume::ORTHOGRAPHIC)
327     proj = get_ortho_projection(rml, rpl, tmb, tpb, n, f);
328   else
329     proj = get_perspective_projection(rml, rpl, tmb, tpb, n, f);
330 }
331 
332 /*!
333   Returns the combined affine and projection matrix.
334 
335   \sa getMatrices(), getCameraSpaceMatrix()
336  */
337 SbDPMatrix
getMatrix(void) const338 SbDPViewVolume::getMatrix(void) const
339 {
340   SbDPMatrix affine, proj;
341   this->getMatrices(affine, proj);
342   return affine.multRight(proj);
343 }
344 
345 /*!
346   Returns a matrix which will translate the view volume camera back to
347   origo, and rotate the camera so it'll point along the negative z axis.
348 
349   Note that the matrix will \a not include the rotation necessary to
350   make the camera up vector point along the positive y axis (i.e.
351   camera roll is not accounted for).
352 
353   \sa getMatrices(), getMatrix()
354  */
355 SbDPMatrix
getCameraSpaceMatrix(void) const356 SbDPViewVolume::getCameraSpaceMatrix(void) const
357 {
358   // Find rotation of projection direction.
359   SbDPRotation pdrot =
360     SbDPRotation(this->projDir, SbVec3d(0.0f, 0.0f, -1.0f));
361 
362   // Combine transforms.
363   SbDPMatrix mat, tmp;
364   mat.setTranslate(-this->projPoint);
365   tmp.setRotate(pdrot);
366   mat.multRight(tmp);
367 
368   return mat;
369 }
370 
371 /*!
372   Project the given 2D point from the projection plane into a 3D line.
373 
374   \a pt coordinates should be normalized to be within [0, 1].
375  */
376 void
projectPointToLine(const SbVec2d & pt,SbDPLine & line) const377 SbDPViewVolume::projectPointToLine(const SbVec2d& pt, SbDPLine& line) const
378 {
379   SbVec3d pt0, pt1;
380   this->projectPointToLine(pt, pt0, pt1);
381   line.setValue(pt0, pt1);
382 }
383 
384 /*!
385   Project the given 2D point from the projection plane into two points
386   defining a 3D line. The first point, \a line0, will be the
387   corresponding point for the projection on the near plane, while \a line1
388   will be the line endpoint, lying in the far plane.
389  */
390 void
projectPointToLine(const SbVec2d & pt,SbVec3d & line0,SbVec3d & line1) const391 SbDPViewVolume::projectPointToLine(const SbVec2d& pt,
392                                    SbVec3d & line0, SbVec3d & line1) const
393 {
394   SbVec3d dx = this->lrf - this->llf;
395   SbVec3d dy = this->ulf - this->llf;
396 
397 #if COIN_DEBUG
398   if (dx.sqrLength() == 0.0f || dy.sqrLength() == 0.0f) {
399     SoDebugError::postWarning("SbDPViewVolume::projectPointToLine",
400                               "invalid frustum");
401     return;
402   }
403 #endif // COIN_DEBUG
404 
405   line0 = this->projPoint + this->llf + dx*pt[0] + dy*pt[1];
406   SbVec3d dir;
407   if (this->type == PERSPECTIVE) {
408     dir = line0 - this->projPoint;
409     // a null vector is ok here, just normalize
410     (void) dir.normalize();
411     line1 = line0 + dir * this->getDepth() / dir.dot(this->projDir);
412   }
413   else {
414     dir = this->projDir;
415     line1 = line0 + dir*this->getDepth();
416   }
417 }
418 
419 /*!
420   Project the \a src point to a normalized set of screen coordinates in
421   the projection plane and place the result in \a dst.
422 
423   It is safe to let \a src and \a dst be the same SbVec3d instance.
424 
425   The z-coordinate of \a dst is monotonically increasing for points
426   closer to the far plane. Note however that this is not a linear
427   relationship, the \a dst z-coordinate is calculated as follows:
428 
429   Orthogonal view:  DSTz = (-2 * SRCz - far - near) / (far - near),
430   Perspective view:  DSTz = (-SRCz * (far - near) - 2*far*near) / (far - near)
431 
432   The returned coordinates (\a dst) are normalized to be in range [0, 1].
433 */
434 void
projectToScreen(const SbVec3d & src,SbVec3d & dst) const435 SbDPViewVolume::projectToScreen(const SbVec3d& src, SbVec3d& dst) const
436 {
437   this->getMatrix().multVecMatrix(src, dst);
438 
439   // coordinates are in range [-1, 1], normalize to [0,1]
440   dst *= 0.5f;
441   dst += SbVec3d(0.5f, 0.5f, 0.5f);
442 }
443 
444 /*!
445   Returns an SbPlane instance which has a normal vector in the opposite
446   direction of which the camera is pointing. This means the
447   plane will be parallel to the near and far clipping planes.
448 
449   \sa getSightPoint()
450  */
451 SbPlane
getPlane(const double distFromEye) const452 SbDPViewVolume::getPlane(const double distFromEye) const
453 {
454   return SbPlane(dp_to_sbvec3f(-this->projDir),
455                  dp_to_sbvec3f(this->projPoint + distFromEye * this->projDir));
456 }
457 
458 /*!
459   Returns the point on the center line-of-sight from the camera position
460   with the given distance.
461 
462   \sa getPlane()
463  */
464 SbVec3d
getSightPoint(const double distFromEye) const465 SbDPViewVolume::getSightPoint(const double distFromEye) const
466 {
467   return this->projPoint + this->projDir * distFromEye;
468 }
469 
470 /*!
471   Return the 3D point which projects to \a normPoint and lies on the
472   plane perpendicular to the camera direction and \a distFromEye
473   distance away from the camera position.
474 
475   \a normPoint should be given in normalized coordinates, where the
476   visible render canvas is covered by the range [0.0, 1.0].
477  */
478 SbVec3d
getPlanePoint(const double distFromEye,const SbVec2d & normPoint) const479 SbDPViewVolume::getPlanePoint(const double distFromEye,
480                               const SbVec2d & normPoint) const
481 {
482   SbVec3d volpt;
483 
484   if(this->getProjectionType() == SbDPViewVolume::ORTHOGRAPHIC) {
485     SbVec3d scr(normPoint[0], normPoint[1], -1.0f);
486 
487     scr[0] -= 0.5f;
488     scr[1] -= 0.5f;
489     scr[0] *= 2.0f;
490     scr[1] *= 2.0f;
491 
492     SbDPMatrix m = this->getMatrix().inverse();
493     m.multVecMatrix(scr, volpt);
494     volpt += (distFromEye - this->getNearDist()) *
495       this->getProjectionDirection();
496   }
497   else {
498     // Find vector pointing in the direction of the normalized 2D
499     // point.
500     SbVec3d dvec =
501       this->llf +
502       (this->lrf - this->llf) * normPoint[0] +
503       (this->ulf - this->llf) * normPoint[1];
504 
505     if (dvec.normalize() == 0.0) {
506 #if COIN_DEBUG
507       SoDebugError::postWarning("SbDPViewVolume::getPlanePoint",
508                                 "Frustum is invalid, point set to the projection point.");
509 #endif // COIN_DEBUG
510       // just set volpt to the projection point
511       volpt = this->getProjectionPoint();
512     }
513     else {
514       // Distance to point.
515       double d = distFromEye/dvec.dot(this->getProjectionDirection());
516 
517       volpt = d * dvec + this->getProjectionPoint();
518     }
519   }
520 
521   return volpt;
522 }
523 
524 /*!
525   Returns a rotation that aligns an object so that its positive x-axis
526   is to the right and its positive y-axis is up in the view volume.
527 
528   If rightangleonly is TRUE, it will create a rotation that aligns the
529   x and y-axis with the closest orthogonal axes to right and up.
530 */
531 SbDPRotation
getAlignRotation(SbBool rightangleonly) const532 SbDPViewVolume::getAlignRotation(SbBool rightangleonly) const
533 {
534   SbVec3d x,y,z;
535   if (rightangleonly) {
536     y = this->ulf - this->llf;
537     y = y.getClosestAxis();
538 
539     x = this->lrf - this->llf;
540     x = x.getClosestAxis();
541 
542     z = x.cross(y);
543     (void) z.normalize();
544   }
545   else {
546     y = this->ulf - this->llf;
547     (void) y.normalize();
548     x = this->lrf - this->llf;
549     (void) x.normalize();
550     z = x.cross(y);
551     (void) z.normalize();
552   }
553 
554   SbDPMatrix m = SbDPMatrix::identity();
555   m[0][0] = x[0];
556   m[0][1] = x[1];
557   m[0][2] = x[2];
558 
559   m[1][0] = y[0];
560   m[1][1] = y[1];
561   m[1][2] = y[2];
562 
563   m[2][0] = z[0];
564   m[2][1] = z[1];
565   m[2][2] = z[2];
566 
567   return SbDPRotation(m);
568 }
569 
570 /*!
571   Given a sphere with center in \a worldCenter and an initial radius of \a 1.0,
572   return the scale factor needed to make this sphere have a \a normRadius
573   radius when projected onto the near clipping plane.
574  */
575 double
getWorldToScreenScale(const SbVec3d & worldCenter,double normRadius) const576 SbDPViewVolume::getWorldToScreenScale(const SbVec3d& worldCenter,
577                                     double normRadius) const
578 {
579 #if COIN_DEBUG
580   if (normRadius < 0.0f) {
581     SoDebugError::postWarning("SbDPViewVolume::getWorldToScreenScale",
582                               "normRadius (%f) should be >=0.0f.", normRadius);
583     return 1.0f;
584   }
585   if (this->getWidth() == 0.0f || this->getHeight() == 0.0f) {
586     SoDebugError::postWarning("SbDPViewVolume::getWorldToScreenScale",
587                               "invalid frustum <%f, %f>",
588                               this->getWidth(), this->getHeight());
589     return 1.0f;
590   }
591 #endif // COIN_DEBUG
592 
593   if(this->getProjectionType() == SbDPViewVolume::ORTHOGRAPHIC) {
594     SbVec3d rightvec = this->lrf - this->llf;
595     return (normRadius * rightvec).length();
596   }
597   else {
598     // Find screen space coordinates of sphere center point and tangent
599     // point.
600     SbVec3d center_scr;
601     this->projectToScreen(worldCenter, center_scr);
602     center_scr[0] += normRadius;
603 
604     // Vectors spanning the projection plane.
605     SbVec3d upvec = this->ulf - this->llf;
606     SbVec3d rightvec = this->lrf - this->llf;
607 
608     // Find projection plane point for the sphere tangent touch point,
609     // which is then used to define the sphere tangent line.
610     SbVec3d ppp = this->projPoint +
611       this->llf + center_scr[0] * rightvec + center_scr[1] * upvec;
612     SbLine tl(dp_to_sbvec3f(this->getProjectionPoint()), dp_to_sbvec3f(ppp));
613 
614     // Define the plane which is cutting the sphere in half and is normal
615     // to the camera direction.
616     SbVec3d sphere_camera_vec = worldCenter - this->getProjectionPoint();
617     SbPlane p = SbPlane(dp_to_sbvec3f(sphere_camera_vec), dp_to_sbvec3f(worldCenter));
618 
619     // Find tangent point of sphere.
620     SbVec3f tangentpt;
621     SbBool result = p.intersect(tl, tangentpt);
622     assert(result != FALSE);
623 
624     // Return radius (which is equal to the scale factor, since we're
625     // dealing with a unit sphere).
626     return (dp_to_sbvec3d(tangentpt) - worldCenter).length();
627   }
628 }
629 
630 /*!
631   Projects the given box onto the projection plane and returns the
632   normalized screen space it occupies.
633  */
634 SbVec2d
projectBox(const SbBox3f & box) const635 SbDPViewVolume::projectBox(const SbBox3f& box) const
636 {
637 #if COIN_DEBUG
638   if (box.isEmpty()) {
639     SoDebugError::postWarning("SbDPViewVolume::projectBox",
640                               "Box is empty.");
641   }
642 #endif // COIN_DEBUG
643 
644   SbVec3d mincorner = dp_to_sbvec3d(box.getMin());
645   SbVec3d maxcorner = dp_to_sbvec3d(box.getMax());
646   SbBox2f span;
647 
648   for(int i=0; i < 2; i++) {
649     for(int j=0; j < 2; j++) {
650       for(int k=0; k < 2; k++) {
651         SbVec3d corner(i ? mincorner[0] : maxcorner[0],
652                        j ? mincorner[1] : maxcorner[1],
653                        k ? mincorner[2] : maxcorner[2]);
654         this->projectToScreen(corner, corner);
655         span.extendBy(SbVec2f(static_cast<float>(corner[0]), static_cast<float>(corner[1])));
656       }
657     }
658   }
659 
660   return SbVec2d(span.getMax()[0] - span.getMin()[0],
661                  span.getMax()[1] - span.getMin()[1]);
662 }
663 
664 /*!
665   Returns a narrowed version of the view volume which is within the
666   given [0, 1] normalized coordinates. The coordinates are taken to be
667   corner points of a normalized "view window" on the near clipping
668   plane.  I.e.:
669 
670   \code
671   SbDPViewVolume view;
672   view.ortho(0, 100, 0, 100, 0.1, 1000);
673   view = view.narrow(0.25, 0.5, 0.75, 1.0);
674   \endcode
675 
676   ..will give a view volume with corner points <25, 75> and <50, 100>.
677 
678   \sa scale(), scaleWidth(), scaleHeight()
679  */
680 SbDPViewVolume
narrow(double left,double bottom,double right,double top) const681 SbDPViewVolume::narrow(double left, double bottom,
682                      double right, double top) const
683 {
684 #if COIN_DEBUG && 0 // debug test disabled, 2001-02-16, pederb
685   if (left<0.0f) {
686     SoDebugError::postWarning("SbDPViewVolume::narrow",
687                               "left coordinate (%f) should be >=0.0f. "
688                               "Clamping to 0.0f.",left);
689     left=0.0f;
690   }
691   if (right>1.0f) {
692     SoDebugError::postWarning("SbDPViewVolume::narrow",
693                               "right coordinate (%f) should be <=1.0f. "
694                               "Clamping to 1.0f.",right);
695     right=1.0f;
696   }
697   if (bottom<0.0f) {
698     SoDebugError::postWarning("SbDPViewVolume::narrow",
699                               "bottom coordinate (%f) should be >=0.0f. "
700                               "Clamping to 0.0f.",bottom);
701     bottom=0.0f;
702   }
703   if (top>1.0f) {
704     SoDebugError::postWarning("SbDPViewVolume::narrow",
705                               "top coordinate (%f) should be <=1.0f. "
706                               "Clamping to 1.0f.",top);
707     top=1.0f;
708   }
709   if (left>right) {
710     SoDebugError::postWarning("SbDPViewVolume::narrow",
711                               "right coordinate (%f) should be larger than "
712                               "left coordinate (%f). Swapping left/right.",
713                               right,left);
714     double tmp=right;
715     right=left;
716     left=tmp;
717   }
718   if (bottom>top) {
719     SoDebugError::postWarning("SbDPViewVolume::narrow",
720                               "top coordinate (%f) should be larger than "
721                               "bottom coordinate (%f). Swapping top/bottom.",
722                               top,bottom);
723     double tmp=top;
724     top=bottom;
725     bottom=tmp;
726   }
727 #endif // COIN_DEBUG
728 
729   SbDPViewVolume nvw = *this;
730 
731   double w = nvw.getWidth();
732   double h = nvw.getHeight();
733 
734   SbVec3d xvec = this->lrf - this->llf;
735   SbVec3d yvec = this->ulf - this->llf;
736 
737   if (yvec.normalize() == 0.0 || xvec.normalize() == 0.0) {
738 #if COIN_DEBUG
739     SoDebugError::postWarning("SbDPViewVolume::narrow",
740                               "View volume was empty before narrowing.");
741 
742 #endif // COIN_DEBUG
743   }
744 
745   nvw.ulf = nvw.llf + (xvec * left * w + yvec * top * h);
746   nvw.lrf =
747     nvw.llf + (xvec * right * w + yvec * bottom * h);
748   nvw.llf += xvec * left * w + yvec * bottom * h;
749 
750   return nvw;
751 }
752 
753 /*!
754 
755   Returns a narrowed version of the view volume which is within the
756   given [0, 1] normalized coordinates. The box x and y coordinates are
757   taken to be corner points of a normalized "view window" on the near
758   clipping plane. The box z coordinates are used to adjust the near
759   and far clipping planes, and should be relative to the current
760   clipping planes. A value of 1.0 is at the current near plane. A
761   value of 0.0 is at the current far plane.
762 */
763 SbDPViewVolume
narrow(const SbBox3f & box) const764 SbDPViewVolume::narrow(const SbBox3f & box) const
765 {
766   SbVec3d bmin = dp_to_sbvec3d(box.getMin());
767   SbVec3d bmax = dp_to_sbvec3d(box.getMax());
768   return this->narrow(bmin[0], bmin[1], bmax[0], bmax[1]).zNarrow(bmax[2], bmin[2]);
769 }
770 
771 // FIXME: bitmap-illustration for function doc which shows how the
772 // frustum is set up wrt the input arguments. 20010919 mortene.
773 /*!
774   Set up the view volume as a rectangular box for orthographic
775   parallel projections. The line of sight will be along the negative
776   z axis, through the center of the plane defined by the point
777   <(right+left)/2, (top+bottom)/2, 0>.
778 
779   \sa perspective().
780 */
781 void
ortho(double left,double right,double bottom,double top,double nearval,double farval)782 SbDPViewVolume::ortho(double left, double right,
783                     double bottom, double top,
784                     double nearval, double farval)
785 {
786 #if defined(COIN_DEBUG) && 0 // disabled 2002-08-30 pederb
787 
788   // These parameter tests are probably incorrect. It is possible to
789   // set left > right etc. in SGI/TGS Inventor, and it should be
790   // possible to do this in Coin also.  pederb.
791 
792   // (Yes, we've actually had user requests for making this possible
793   // after first disallowing it. I don't know what kind of effects
794   // they are using this for, and I'm not sure I want to know.. :-})
795   // mortene.
796 
797 
798   if (left>right) {
799     SoDebugError::postWarning("SbDPViewVolume::ortho",
800                               "right coordinate (%f) should be larger than "
801                               "left coordinate (%f). Swapping left/right.",
802                               right,left);
803     double tmp=right;
804     right=left;
805     left=tmp;
806   }
807   if (bottom>top) {
808     SoDebugError::postWarning("SbDPViewVolume::ortho",
809                               "top coordinate (%f) should be larger than "
810                               "bottom coordinate (%f). Swapping bottom/top.",
811                               top,bottom);
812     double tmp=top;
813     top=bottom;
814     bottom=tmp;
815   }
816   if (nearval>farval) {
817     SoDebugError::postWarning("SbDPViewVolume::ortho",
818                               "far coordinate (%f) should be larger than near "
819                               "coordinate (%f). Swapping near/far.",farval,nearval);
820     double tmp=farval;
821     farval=nearval;
822     nearval=tmp;
823   }
824 #endif // disabled
825 
826   this->type = SbDPViewVolume::ORTHOGRAPHIC;
827   this->projPoint.setValue(0.0f, 0.0f, 0.0f);
828   this->projDir.setValue(0.0f, 0.0f, -1.0f);
829   this->nearDist = nearval;
830   this->nearToFar = farval - nearval;
831   this->llf.setValue(left, bottom, -nearval);
832   this->lrf.setValue(right, bottom, -nearval);
833   this->ulf.setValue(left, top, -nearval);
834 }
835 
836 // FIXME: bitmap-illustration for function doc which shows how the
837 // frustum is set up wrt the input arguments. 20010919 mortene.
838 /*!
839   Set up the view volume for perspective projections. The line of
840   sight will be through origo along the negative z axis.
841 
842   \sa ortho().
843 */
844 void
perspective(double fovy,double aspect,double nearval,double farval)845 SbDPViewVolume::perspective(double fovy, double aspect,
846                             double nearval, double farval)
847 {
848 #if COIN_DEBUG
849   if (fovy<0.0f || fovy > M_PI) {
850     SoDebugError::postWarning("SbDPViewVolume::perspective",
851                               "Field of View 'fovy' (%f) is out of bounds "
852                               "[0,PI]. Clamping to be within bounds.",fovy);
853     if (fovy<0.0f) fovy=0.0f;
854     else if (fovy>M_PI) fovy=M_PI;
855   }
856 
857 #if 0 // obsoleted 2003-02-03 pederb. A negative aspect ratio is ok
858   if (aspect<0.0f) {
859     SoDebugError::postWarning("SbDPViewVolume::perspective",
860                               "Aspect ratio 'aspect' (%d) should be >=0.0f. "
861                               "Clamping to 0.0f.",aspect);
862     aspect=0.0f;
863   }
864 #endif // obsoleted
865 
866   if (nearval>farval) {
867     SoDebugError::postWarning("SbDPViewVolume::perspective",
868                               "far coordinate (%f) should be larger than "
869                               "near coordinate (%f). Swapping near/far.",
870                               farval,nearval);
871     double tmp=farval;
872     farval=nearval;
873     nearval=tmp;
874   }
875 #endif // COIN_DEBUG
876 
877   this->type = SbDPViewVolume::PERSPECTIVE;
878   this->projPoint.setValue(0.0f, 0.0f, 0.0f);
879   this->projDir.setValue(0.0f, 0.0f, -1.0f);
880   this->nearDist = nearval;
881   this->nearToFar = farval - nearval;
882 
883   double top = nearval * double(tan(fovy/2.0f));
884   double bottom = -top;
885   double left = bottom * aspect;
886   double right = -left;
887 
888   this->llf.setValue(left, bottom, -nearval);
889   this->lrf.setValue(right, bottom, -nearval);
890   this->ulf.setValue(left, top, -nearval);
891 }
892 
893 /*!
894   Set up the frustum for perspective projection. This is an
895   alternative to perspective() that lets you specify any kind of view
896   volumes (e.g. off center volumes). It has the same arguments and
897   functionality as the corresponding OpenGL glFrustum() function.
898 
899   \sa perspective()
900 */
901 void
frustum(double left,double right,double bottom,double top,double nearval,double farval)902 SbDPViewVolume::frustum(double left, double right,
903                         double bottom, double top,
904                         double nearval, double farval)
905 {
906   this->type = SbDPViewVolume::PERSPECTIVE;
907   this->projPoint.setValue(0.0f, 0.0f, 0.0f);
908   this->projDir.setValue(0.0f, 0.0f, -1.0f);
909   this->nearDist = nearval;
910   this->nearToFar = farval - nearval;
911 
912   this->llf.setValue(left, bottom, -nearval);
913   this->lrf.setValue(right, bottom, -nearval);
914   this->ulf.setValue(left, top, -nearval);
915 }
916 
917 /*!
918   Rotate the direction which the camera is pointing in.
919 
920   \sa translateCamera().
921  */
922 void
rotateCamera(const SbDPRotation & q)923 SbDPViewVolume::rotateCamera(const SbDPRotation& q)
924 {
925   SbDPMatrix mat;
926   mat.setRotate(q);
927 
928   mat.multDirMatrix(this->projDir, this->projDir);
929   mat.multDirMatrix(this->llf, this->llf);
930   mat.multDirMatrix(this->lrf, this->lrf);
931   mat.multDirMatrix(this->ulf, this->ulf);
932 }
933 
934 /*!
935   Translate the camera position of the view volume.
936 
937   \sa rotateCamera().
938  */
939 void
translateCamera(const SbVec3d & v)940 SbDPViewVolume::translateCamera(const SbVec3d & v)
941 {
942   this->projPoint += v;
943 }
944 
945 /*!
946   Return the vector pointing from the center of the view volume towards
947   the camera. This is just the vector pointing in the opposite direction
948   of \a getProjectionDirection().
949 
950   \sa getProjectionDirection().
951  */
952 SbVec3d
zVector(void) const953 SbDPViewVolume::zVector(void) const
954 {
955   return -this->projDir;
956 }
957 
958 /*!
959   Return a copy SbDPViewVolume with narrowed depth by supplying parameters
960   for new near and far clipping planes.
961 
962   \a nearval and \a farval should be relative to the current clipping
963   planes. A value of 1.0 is at the current near plane. A value of
964   0.0 is at the current far plane.
965 
966   \sa zVector().
967 */
968 SbDPViewVolume
zNarrow(double nearval,double farval) const969 SbDPViewVolume::zNarrow(double nearval, double farval) const
970 {
971   SbDPViewVolume narrowed = *this;
972 
973   narrowed.nearDist = this->nearDist + (1.0f - nearval) * this->nearToFar;
974   narrowed.nearToFar = this->nearDist + this->nearToFar * (1.0f - farval);
975 
976   SbVec3d dummy;
977   this->getPlaneRectangle(narrowed.nearDist - this->nearDist,
978                           narrowed.llf,
979                           narrowed.lrf,
980                           narrowed.ulf,
981                           dummy);
982   return narrowed;
983 }
984 
985 /*!
986   Scale width and height of viewing frustum by the given ratio around the
987   projection plane center axis.
988 
989   \sa scaleWidth(), scaleHeight().
990  */
991 void
scale(double factor)992 SbDPViewVolume::scale(double factor)
993 {
994 #if COIN_DEBUG
995   if (factor<0.0f) {
996     SoDebugError::postWarning("SbDPViewVolume::scale",
997                               "Scale factor (%f) should be >=0.0f. Clamping "
998                               "to 0.0f.",factor);
999     factor=0.0f;
1000   }
1001 #endif // COIN_DEBUG
1002 
1003   this->scaleWidth(factor);
1004   this->scaleHeight(factor);
1005 }
1006 
1007 /*!
1008   Scale width of viewing frustum by the given ratio around the vertical
1009   center axis in the projection plane.
1010 
1011   \sa scale(), scaleHeight().
1012  */
1013 void
scaleWidth(double ratio)1014 SbDPViewVolume::scaleWidth(double ratio)
1015 {
1016 #if COIN_DEBUG
1017   if (ratio<0.0f) {
1018     SoDebugError::postWarning("SbDPViewVolume::scaleWidth",
1019                               "Scale factor (%f) should be >=0.0f. "
1020                               "Clamping to 0.0f.",ratio);
1021     ratio=0.0f;
1022   }
1023 #endif // COIN_DEBUG
1024 
1025   double w = this->getWidth();
1026   double neww = w * ratio;
1027   double wdiff = (neww - w)/2.0f;
1028 
1029   SbVec3d xvec = this->lrf - this->llf;
1030   if (xvec.normalize() == 0.0) {
1031 #if COIN_DEBUG
1032     SoDebugError::postWarning("SbDPViewVolume::scaleWidth",
1033                               "View volume had no width before scaling.");
1034 #endif // COIN_DEBUG
1035   }
1036   SbVec3d diffvec = xvec * wdiff;
1037 
1038   this->llf -= diffvec;
1039   this->ulf -= diffvec;
1040   this->lrf += diffvec;
1041 }
1042 
1043 /*!
1044   Scale height of viewing frustum by the given ratio around the horizontal
1045   center axis in the projection plane.
1046 
1047   \sa scale(), scaleWidth().
1048  */
1049 void
scaleHeight(double ratio)1050 SbDPViewVolume::scaleHeight(double ratio)
1051 {
1052 #if COIN_DEBUG
1053   if (ratio<0.0f) {
1054     SoDebugError::postWarning("SbDPViewVolume::scaleHeight",
1055                               "Scale factor (%f) should be >=0.0f. "
1056                               "Clamping to 0.0f.",ratio);
1057     ratio=0.0f;
1058   }
1059 #endif // COIN_DEBUG
1060 
1061   double h = this->getHeight();
1062   double newh = h * ratio;
1063   double hdiff = (newh - h)/2.0f;
1064 
1065   SbVec3d upvec = this->ulf - this->llf;
1066   if (upvec.normalize() == 0.0) {
1067 #if COIN_DEBUG
1068     SoDebugError::postWarning("SbDPViewVolume::scaleHeight",
1069                               "View volume had no height before scaling.");
1070 #endif // COIN_DEBUG
1071   }
1072   SbVec3d diffvec = upvec * hdiff;
1073 
1074   this->llf -= diffvec;
1075   this->ulf += diffvec;
1076   this->lrf -= diffvec;
1077 }
1078 
1079 /*!
1080   Return current view volume projection type, which can be
1081   either \a ORTHOGRAPHIC or \a PERSPECTIVE.
1082 
1083   \sa SbDPViewVolume::ProjectionType
1084  */
1085 SbDPViewVolume::ProjectionType
getProjectionType(void) const1086 SbDPViewVolume::getProjectionType(void) const
1087 {
1088   return this->type;
1089 }
1090 
1091 /*!
1092   Returns coordinates of center point in the projection plane.
1093  */
1094 const SbVec3d&
getProjectionPoint(void) const1095 SbDPViewVolume::getProjectionPoint(void) const
1096 {
1097   return this->projPoint;
1098 }
1099 
1100 /*!
1101   Returns the direction of projection, i.e. the direction the camera is
1102   pointing.
1103 
1104   \sa getNearDist().
1105  */
1106 const SbVec3d&
getProjectionDirection(void) const1107 SbDPViewVolume::getProjectionDirection(void) const
1108 {
1109   return this->projDir;
1110 }
1111 
1112 /*!
1113   Returns distance from projection plane to near clipping plane.
1114 
1115   \sa getProjectionDirection().
1116  */
1117 double
getNearDist(void) const1118 SbDPViewVolume::getNearDist(void) const
1119 {
1120   return this->nearDist;
1121 }
1122 
1123 /*!
1124   Returns width of viewing frustum in the projection plane.
1125 
1126   \sa getHeight(), getDepth().
1127 */
1128 double
getWidth(void) const1129 SbDPViewVolume::getWidth(void) const
1130 {
1131   return (this->lrf - this->llf).length();
1132 }
1133 
1134 /*!
1135   Returns height of viewing frustum in the projection plane.
1136 
1137   \sa getWidth(), getDepth().
1138 */
1139 double
getHeight(void) const1140 SbDPViewVolume::getHeight(void) const
1141 {
1142   return (this->ulf - this->llf).length();
1143 }
1144 
1145 /*!
1146   Returns depth of viewing frustum, i.e. the distance from the near clipping
1147   plane to the far clipping plane.
1148 
1149   \sa getWidth(), getHeight().
1150  */
1151 double
getDepth(void) const1152 SbDPViewVolume::getDepth(void) const
1153 {
1154   return this->nearToFar;
1155 }
1156 
1157 /*!
1158   Dump the state of this object to the \a file stream. Only works in
1159   debug version of library, method does nothing in an optimized compile.
1160  */
1161 void
print(FILE * fp) const1162 SbDPViewVolume::print(FILE * fp) const
1163 {
1164 #if COIN_DEBUG
1165   fprintf( fp, "  projtype: %d\n", static_cast<int>(this->getProjectionType()) );
1166   fprintf( fp, "  projpt:   " );
1167   this->getProjectionPoint().print(fp);
1168   fprintf( fp, "\n" );
1169   fprintf( fp, "  projdir:  " );
1170   this->getProjectionDirection().print(fp);
1171   fprintf( fp, "\n" );
1172   fprintf( fp, "  neardist: %f\n", this->getNearDist() );
1173   fprintf( fp, "  width:    %f\n", this->getWidth() );
1174   fprintf( fp, "  height:   %f\n", this->getHeight() );
1175   fprintf( fp, "  depth:    %f\n", this->getDepth() );
1176   fprintf( fp, "    llf:    " );
1177   this->llf.print(fp);
1178   fprintf( fp, "\n" );
1179   fprintf( fp, "    lrf:    " );
1180   this->lrf.print(fp);
1181   fprintf( fp, "\n" );
1182   fprintf( fp, "    ulf:    " );
1183   this->ulf.print(fp);
1184   fprintf( fp, "\n" );
1185 #endif // COIN_DEBUG
1186 }
1187 
1188 /*!
1189   Returns the six planes defining the view volume in the following
1190   order: left, bottom, right, top, near, far. Plane normals are
1191   directed into the view volume.
1192 
1193   This method is an extension for Coin, and is not available in the
1194   original Open Inventor.
1195 */
1196 void
getViewVolumePlanes(SbPlane planes[6]) const1197 SbDPViewVolume::getViewVolumePlanes(SbPlane planes[6]) const
1198 {
1199   SbVec3d far_ll;
1200   SbVec3d far_lr;
1201   SbVec3d far_ul;
1202   SbVec3d far_ur;
1203 
1204   this->getPlaneRectangle(this->nearToFar, far_ll, far_lr, far_ul, far_ur);
1205   SbVec3d near_ur = this->ulf + (this->lrf-this->llf);
1206 
1207   SbVec3f f_ulf = dp_to_sbvec3f(this->ulf + this->projPoint);
1208   SbVec3f f_llf = dp_to_sbvec3f(this->llf + this->projPoint);
1209   SbVec3f f_lrf = dp_to_sbvec3f(this->lrf + this->projPoint);
1210   SbVec3f f_near_ur = dp_to_sbvec3f(near_ur + this->projPoint);
1211   SbVec3f f_far_ll = dp_to_sbvec3f(far_ll + this->projPoint);
1212   SbVec3f f_far_lr = dp_to_sbvec3f(far_lr + this->projPoint);
1213   SbVec3f f_far_ul = dp_to_sbvec3f(far_ul + this->projPoint);
1214   SbVec3f f_far_ur = dp_to_sbvec3f(far_ur + this->projPoint);
1215 
1216   planes[0] = SbPlane(f_ulf, f_llf, f_far_ll);  // left
1217   planes[1] = SbPlane(f_llf, f_lrf, f_far_lr); // bottom
1218   planes[2] = SbPlane(f_lrf, f_near_ur, f_far_ur); // right
1219   planes[3] = SbPlane(f_near_ur, f_ulf, f_far_ul); // top
1220   planes[4] = SbPlane(f_ulf, f_near_ur, f_lrf); // near
1221   planes[5] = SbPlane(f_far_ll, f_far_lr, f_far_ur); // far
1222 
1223   // check for inverted view volume (negative aspectRatio)
1224   if (!planes[0].isInHalfSpace(f_lrf)) {
1225     SbVec3f n;
1226     float D;
1227 
1228     n = planes[0].getNormal();
1229     D = planes[0].getDistanceFromOrigin();
1230     planes[0] = SbPlane(-n, -D);
1231 
1232     n = planes[2].getNormal();
1233     D = planes[2].getDistanceFromOrigin();
1234     planes[2] = SbPlane(-n, -D);
1235   }
1236   if (!planes[1].isInHalfSpace(f_near_ur)) {
1237     SbVec3f n;
1238     float D;
1239 
1240     n = planes[1].getNormal();
1241     D = planes[1].getDistanceFromOrigin();
1242     planes[1] = SbPlane(-n, -D);
1243 
1244     n = planes[3].getNormal();
1245     D = planes[3].getDistanceFromOrigin();
1246     planes[3] = SbPlane(-n, -D);
1247 
1248   }
1249 
1250   if (!planes[4].isInHalfSpace(f_far_ll)) {
1251     SbVec3f n;
1252     float D;
1253 
1254     n = planes[4].getNormal();
1255     D = planes[4].getDistanceFromOrigin();
1256     planes[4] = SbPlane(-n, -D);
1257 
1258     n = planes[5].getNormal();
1259     D = planes[5].getDistanceFromOrigin();
1260     planes[5] = SbPlane(-n, -D);
1261 
1262   }
1263 
1264 }
1265 
1266 /*!
1267   Transform the viewing volume by \a matrix.
1268  */
1269 void
transform(const SbDPMatrix & matrix)1270 SbDPViewVolume::transform(const SbDPMatrix & matrix)
1271 {
1272   SbVec3d oldprojpt = this->projPoint;
1273   SbVec3d newprojpt;
1274   SbVec3d newllf;
1275   SbVec3d newlrf;
1276   SbVec3d newulf;
1277   matrix.multVecMatrix(oldprojpt, newprojpt);
1278 
1279 
1280   // need to translate frustum point with the projection point before
1281   // transforming, then translate back afterwards.
1282   matrix.multVecMatrix(this->llf+oldprojpt, newllf);
1283   newllf -= newprojpt;
1284 
1285   matrix.multVecMatrix(this->lrf+oldprojpt, newlrf);
1286   newlrf -= newprojpt;
1287 
1288   matrix.multVecMatrix(this->ulf+oldprojpt, newulf);
1289   newulf -= newprojpt;
1290 
1291   // Construct and tranform nearpt and farpt to find the new near and
1292   // far values.
1293   SbVec3d nearpt;
1294   SbVec3d farpt;
1295   matrix.multVecMatrix(oldprojpt + this->nearDist * this->projDir,
1296                        nearpt);
1297 
1298   double fardist = this->nearDist + this->nearToFar;
1299   matrix.multVecMatrix(oldprojpt + fardist * this->projDir, farpt);
1300 
1301   matrix.multDirMatrix(this->projDir, this->projDir);
1302   this->projPoint = newprojpt;
1303   this->llf = newllf;
1304   this->ulf = newulf;
1305   this->lrf = newlrf;
1306   SbDPPlane projPlane(this->projDir, this->projPoint);
1307   this->nearDist = projPlane.getDistance(nearpt);
1308   this->nearToFar = (farpt-nearpt).length();
1309 }
1310 
1311 /*!
1312   Returns the view up vector for this view volume. It's a vector
1313   which is perpendicular to the projection direction, and parallel and
1314   oriented in the same direction as the vector from the lower left
1315   corner to the upper left corner of the near plane.
1316 */
1317 SbVec3d
getViewUp(void) const1318 SbDPViewVolume::getViewUp(void) const
1319 {
1320   SbVec3d v = this->ulf - this->llf;
1321   if (v.normalize() == 0.0) {
1322 #if COIN_DEBUG
1323     SoDebugError::postWarning("SbDPViewVolume::getViewUp",
1324                               "View volume is empty.");
1325 #endif // COIN_DEBUG
1326   }
1327   return v;
1328 }
1329 
1330 //
1331 // Returns the four points defining the view volume rectangle at the
1332 // specified distance from the near plane, towards the far plane. The
1333 // points are returned in normalized view volume coordinates
1334 // (projPoint is not added).
1335 void
getPlaneRectangle(const double distance,SbVec3d & lowerleft,SbVec3d & lowerright,SbVec3d & upperleft,SbVec3d & upperright) const1336 SbDPViewVolume::getPlaneRectangle(const double distance, SbVec3d & lowerleft,
1337                                   SbVec3d & lowerright,
1338                                   SbVec3d & upperleft,
1339                                   SbVec3d & upperright) const
1340 {
1341   SbVec3d near_ur = this->ulf + (this->lrf-this->llf);
1342 
1343 #if COIN_DEBUG
1344   if (this->llf == SbVec3d(0.0, 0.0, 0.0) ||
1345       this->lrf == SbVec3d(0.0, 0.0, 0.0) ||
1346       this->ulf == SbVec3d(0.0, 0.0, 0.0) ||
1347       near_ur == SbVec3d(0.0, 0.0, 0.0)) {
1348     SoDebugError::postWarning("SbDPViewVolume::getPlaneRectangle",
1349                               "Invalid frustum.");
1350 
1351   }
1352 #endif // COIN_DEBUG
1353 
1354   if (this->type == PERSPECTIVE) {
1355     double depth = this->nearDist + distance;
1356     SbVec3d dir;
1357     dir = this->llf;
1358     (void) dir.normalize(); // safe to normalize here
1359     lowerleft = dir * depth / dir.dot(this->projDir);
1360 
1361     dir = this->lrf;
1362     dir.normalize(); // safe to normalize here
1363     lowerright = dir * depth / dir.dot(this->projDir);
1364 
1365     dir = this->ulf;
1366     (void) dir.normalize(); // safe to normalize here
1367     upperleft = dir * depth / dir.dot(this->projDir);
1368 
1369     dir = near_ur;
1370     (void) dir.normalize(); // safe to normalize here
1371     upperright = dir * depth / dir.dot(this->projDir);
1372   }
1373   else {
1374     lowerleft = this->llf + this->projDir * distance;
1375     lowerright = this->lrf + this->projDir * distance;
1376     upperleft = this->ulf + this->projDir * distance;
1377     upperright = near_ur + this->projDir * distance;
1378   }
1379 }
1380 
1381 void
copyValues(SbViewVolume & vv)1382 SbDPViewVolume::copyValues(SbViewVolume & vv)
1383 {
1384   vv.type = static_cast<SbViewVolume::ProjectionType>(this->type);
1385   vv.projPoint = dp_to_sbvec3f(this->projPoint);
1386   vv.projDir = dp_to_sbvec3f(this->projDir);
1387   vv.nearDist = static_cast<float>(this->nearDist);
1388   vv.nearToFar = static_cast<float>(this->nearToFar);
1389   vv.llf = dp_to_sbvec3f(this->llf + this->projPoint);
1390   vv.lrf = dp_to_sbvec3f(this->lrf + this->projPoint);
1391   vv.ulf = dp_to_sbvec3f(this->ulf + this->projPoint);
1392 }
1393