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