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 SbProjector SbProjector.h Inventor/projectors/SbProjector.h
35   \brief The SbProjector class is the abstract base projector class.
36 
37   \ingroup projectors
38 
39   Projectors are used in the Coin library for mapping 2D coordinates
40   (typically from the position of the mouse cursor in the rendering
41   window) to 3D "world" coordinates.
42 
43   Mapping 2D coordinates to 3D coordinates is something which is done
44   extensively in the dragger classes, to provide the user with a
45   convenient and natural way of interacting with the 3D geometry of
46   scenes.
47 
48   For a usage example, see the class documentation for
49   SbSphereSheetProjector.
50 
51   The application programmer should normally not need to care about
52   the projector classes, unless there are special needs in the
53   application.
54 
55   \sa SoDragger
56 */
57 
58 
59 #include <Inventor/projectors/SbProjector.h>
60 #include <Inventor/SbBox3f.h>
61 #include <Inventor/SbVec2f.h>
62 #include <Inventor/SbPlane.h>
63 #include <cassert>
64 #include "coindefs.h"
65 
66 /*!
67   \fn SbProjector::~SbProjector()
68 
69   Destructor is protected, as this is an abstract class.
70  */
71 
72 /*!
73   \fn virtual SbVec3f SbProjector::project(const SbVec2f & point)
74 
75   Project the 2D \a point from normalized viewport coordinates to a 3D
76   point. The mapping will be done in accordance with the type of the
77   projector.
78  */
79 /*!
80   \fn virtual SbProjector * SbProjector::copy(void) const
81 
82   Construct and return a copy of this projector. The caller is
83   responsible for destructing the new instance.
84 
85   \DANGEROUS_ALLOC_RETURN
86  */
87 
88 /*!
89   \var SbProjector::viewVol
90 
91   The viewVol definition.
92 */
93 /*!
94   \var SbProjector::worldToWorking
95 
96   The matrix which converts from world coordinates to coordinates in
97   the projector's local coordinate system.
98 */
99 /*!
100   \var SbProjector::workingToWorld
101 
102   The matrix which converts from coordinates in the projector's local
103   coordinate system to world coordinates.
104 */
105 
106 
107 /*!
108   The constructor initializes the workingspace matrix to an identity
109   matrix.
110  */
SbProjector(void)111 SbProjector::SbProjector(void)
112 {
113   this->worldToWorking.makeIdentity();
114   this->workingToWorld.makeIdentity();
115 }
116 
117 /*!
118   Set the viewing volume the projections will take place in.
119 
120   \sa getViewVolume()
121  */
122 void
setViewVolume(const SbViewVolume & vol)123 SbProjector::setViewVolume(const SbViewVolume & vol)
124 {
125   this->viewVol = vol;
126 }
127 
128 /*!
129   Return the current viewing volume used by the projections.
130 
131   \sa setViewVolume()
132  */
133 const SbViewVolume &
getViewVolume(void) const134 SbProjector::getViewVolume(void) const
135 {
136   return this->viewVol;
137 }
138 
139 /*!
140   Sets the matrix used for converting from the projector's coordinate
141   system to the world coordinate system.
142  */
143 void
setWorkingSpace(const SbMatrix & space)144 SbProjector::setWorkingSpace(const SbMatrix & space)
145 {
146   this->workingToWorld = space;
147   this->worldToWorking = space.inverse();
148 }
149 
150 /*!
151   Returns projector-to-world matrix.
152 
153   \sa setWorkingSpace()
154  */
155 const SbMatrix &
getWorkingSpace(void) const156 SbProjector::getWorkingSpace(void) const
157 {
158   return this->workingToWorld;
159 }
160 
161 /*!
162   From the 2D \a point in normalized screenspace coordinates,
163   calculate the line passing through the scene.
164 
165   Typically used for tracking intersection points for the mouse
166   cursor.
167  */
168 SbLine
getWorkingLine(const SbVec2f & point) const169 SbProjector::getWorkingLine(const SbVec2f & point) const
170 {
171   SbLine l;
172   this->viewVol.projectPointToLine(point, l);
173   this->worldToWorking.multLineMatrix(l, l);
174   return l;
175 }
176 
177 /*!
178   Finds the unit cube vanishing distance for the current projector
179   view volume.  The view volume must be a perspective view
180   volume.
181 
182   This method was not part of the Inventor v2.1 API, and is an
183   extension specific to Coin.
184 
185   \since Coin 1.1
186 */
187 float
findVanishingDistance(void) const188 SbProjector::findVanishingDistance(void) const
189 {
190   const SbViewVolume & vv = this->viewVol;
191 
192   // FIXME: find a proper algorithm to calculate the vanishing
193   // distance. Now we just use an incremental algorithm to detect when
194   // the projected box is less than one pixel on a screen with height
195   // = 512 pixels. pederb, 2001-10-11
196   assert(vv.getProjectionType() == SbViewVolume::PERSPECTIVE);
197   float depth = vv.getHeight();
198 
199   // used to break out if we get too many iterations. Will probably
200   // never happen, but it's here just in case something bad happens.
201   int cnt = 0;
202 
203   float unit = depth * 0.25f;
204 
205   SbBox3f unitbox(-unit, -unit, -unit, unit, unit, unit);
206   SbVec3f projdir = this->viewVol.getProjectionDirection();
207   SbMatrix m;
208   m.setTranslate(projdir * depth);
209   SbBox3f box = unitbox;
210   box.transform(m);
211 
212   SbVec2f siz = vv.projectBox(box);
213   while (cnt < 64 && (siz[1] > (1.0f / 512.0f))) {
214     depth *= 2.0f;
215     m.setTranslate(projdir * depth);
216     SbBox3f box = unitbox;
217     box.transform(m);
218     siz = vv.projectBox(box);
219     cnt++;
220   }
221   return depth;
222 }
223 
224 /*!
225   Verifies that \a projpt is a valid projection for the current view
226   volume. For perspective view volumes, it does this by checking that
227   the projection point is in front of the eye plane. For orthographic
228   projections, this method always returns \e TRUE.
229 
230   This method was not part of the Inventor v2.1 API, and is an
231   extension specific to Coin.
232 
233   \since Coin 1.1
234 */
235 SbBool
verifyProjection(const SbVec3f & projpt) const236 SbProjector::verifyProjection(const SbVec3f & projpt) const
237 {
238   if (this->viewVol.getProjectionType() == SbViewVolume::PERSPECTIVE) {
239     SbPlane eyeplane = this->viewVol.getPlane(0.0f);
240     SbVec3f wrld;
241     this->workingToWorld.multVecMatrix(projpt, wrld);
242     if (this->viewVol.getNearDist() > 0.0f) {
243       if (eyeplane.isInHalfSpace(wrld)) return FALSE;
244     } else {
245       // eye plane for reverse perspective view volume lies behind the scene
246       if (!eyeplane.isInHalfSpace(wrld)) return FALSE;
247     }
248   }
249   return TRUE;
250 }
251 
252 /*!
253   Try projecting the 2D \a point from normalized viewport coordinates to a 3D
254   point. The mapping will be done in accordance with the type of the
255   projector.
256 
257   If the projection can't be done safely (for instance when the
258   projection plane or line is parallel to the view volume projection),
259   this function should return FALSE.
260 
261   Default implementation will call project() and always return TRUE,
262   but subclasses can override this behavior to support safe
263   projections.
264 
265   \since Coin 3.0
266 */
267 SbBool
tryProject(const SbVec2f & point,const float COIN_UNUSED_ARG (epsilon),SbVec3f & result)268 SbProjector::tryProject(const SbVec2f & point, const float COIN_UNUSED_ARG(epsilon), SbVec3f & result)
269 {
270   result = this->project(point);
271   return TRUE;
272 }
273