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 SoLOD SoLOD.h Inventor/nodes/SoLOD.h
35   \brief The SoLOD class is used to choose a child based distance between viewer and object.
36 
37   \ingroup nodes
38 
39   The class documentation for the SoLOD node class would be similar
40   enough to that of SoLevelOfDetail that we will refer you to look at
41   that one first. It will explain the general principles of what a
42   level-of-detail mechanism is, and why and how to use it.
43 
44   (The main difference between SoLOD and SoLevelOfDetail is that SoLOD
45   uses the speedier "distance-to-viewer" technique for implementing
46   level-of-detail functionality, versus the more correct (but
47   potentially slower) "projected-bbox-area" technique used by
48   SoLevelOfDetail.)
49 
50   Here's a mockup example (in Inventor file format style, but easily
51   converted to code) that shows how to use this node:
52 
53   \code
54   LOD {
55      range [ 10, 20, 30, 40 ]
56 
57      Sphere { }
58      Cylinder { }
59      Cone { }
60      Cube { }
61      Info { }
62   }
63   \endcode
64 
65   For the sub-scenegraph above, when the LOD-object is less than 10
66   units away from the viewpoint camera, an SoSphere will be shown. For
67   distances 10 - 20 units away, this will be changed to the
68   SoCylinder, and so on. For distances of \e more than 40 units from
69   the camera, an SoInfo node will be traversed / rendered -- ie,
70   nothing will be shown. (This is a common "trick" used to optimize
71   rendering when models get far enough away from the camera that we
72   want to remove them completely).
73 
74   Note that when using an SoOrthographicCamera in the examiner viewers
75   of the SoQt, SoWin and SoXt libraries, it will seem like the SoLOD
76   node is not working when using the "Zoom" functionality. What
77   happens is that an SoOrthographicCamera is not actually moved, the
78   "zoom" effect is accomplished simply by changing its height-of-view
79   setting (i.e. the value of the SoOrthographicCamera::height field),
80   but its position remains constant. So the distance to the camera
81   will not change, which means SoLOD will pick the same child, no
82   matter how much one "zooms".
83 
84   (The SoLevelOfDetail node uses the screen space area of the object
85   to decide when to switch children, so that node will still work with
86   SoOrthographicCamera.)
87 
88   <b>FILE FORMAT/DEFAULTS:</b>
89   \code
90     LOD {
91         center 0 0 0
92         range [  ]
93     }
94   \endcode
95 
96   \since SGI Inventor 2.1
97   \sa SoLevelOfDetail
98 */
99 
100 // *************************************************************************
101 
102 #include <Inventor/nodes/SoLOD.h>
103 
104 #include <Inventor/misc/SoChildList.h>
105 #include <Inventor/actions/SoGLRenderAction.h>
106 #include <Inventor/actions/SoCallbackAction.h>
107 #include <Inventor/elements/SoViewVolumeElement.h>
108 #include <Inventor/elements/SoModelMatrixElement.h>
109 #include <Inventor/elements/SoGLCacheContextElement.h>
110 
111 #include "nodes/SoSubNodeP.h"
112 #include "nodes/SoSoundElementHelper.h"
113 #include "profiler/SoNodeProfiling.h"
114 
115 // *************************************************************************
116 
117 /*!
118   \var SoMFFloat SoLOD::range
119 
120   The distance ranges which decides when to use each child for
121   traversal / rendering. See usage example in main class documentation
122   of SoLOD for an explanation of how this vector should be set up
123   correctly.
124 
125   By default this vector just contains a single value 0.0f.
126 */
127 /*!
128   \var SoSFVec3f SoLOD::center
129 
130   This vector represents an offset within the object from the
131   geometric center point to the center point the application
132   programmer would actually like the distance between the viewer and
133   the object to be calculated from.
134 
135   Default value is [0, 0, 0]. It is usually not necessary to change
136   this field.
137 */
138 
139 // *************************************************************************
140 
141 class SoLODP : public SoSoundElementHelper
142 {
143 public:
SoLODP(SoLOD * master)144   SoLODP(SoLOD * master) : master(master) {};
145   SoLOD *master;
146 };
147 
148 #define PRIVATE(p) ((p)->pimpl)
149 #define PUBLIC(p) ((p)->master)
150 
151 SO_NODE_SOURCE(SoLOD);
152 
153 /*!
154   Default constructor.
155 */
SoLOD(void)156 SoLOD::SoLOD(void)
157 {
158   this->commonConstructor();
159 }
160 
161 /*!
162   Constructor.
163 
164   The argument should be the approximate number of children which is
165   expected to be inserted below this node. The number need not be
166   exact, as it is only used as a hint for better memory resource
167   allocation.
168 */
SoLOD(int numchildren)169 SoLOD::SoLOD(int numchildren)
170   : inherited(numchildren)
171 {
172   this->commonConstructor();
173 }
174 
175 // private
176 void
commonConstructor(void)177 SoLOD::commonConstructor(void)
178 {
179   PRIVATE(this) = new SoLODP(this);
180 
181   SO_NODE_INTERNAL_CONSTRUCTOR(SoLOD);
182 
183   SO_NODE_ADD_FIELD(center, (SbVec3f(0, 0, 0)));
184   SO_NODE_ADD_FIELD(range, (0.0f));
185 
186   // Make multivalue field empty, as that is the default.
187   this->range.setNum(0);
188   // So it's not written in its default state on SoWriteAction
189   // traversal.
190   this->range.setDefault(TRUE);
191 }
192 
193 /*!
194   Destructor.
195 */
~SoLOD()196 SoLOD::~SoLOD()
197 {
198   delete PRIVATE(this);
199 }
200 
201 // Documented in superclass.
202 void
initClass(void)203 SoLOD::initClass(void)
204 {
205   SO_NODE_INTERNAL_INIT_CLASS(SoLOD, SO_FROM_INVENTOR_2_1|SoNode::VRML1);
206 }
207 
208 
209 // Documented in superclass.
210 void
doAction(SoAction * action)211 SoLOD::doAction(SoAction *action)
212 {
213   int numindices;
214   const int * indices;
215   SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
216   if (pathcode == SoAction::IN_PATH) {
217     this->children->traverseInPath(action, numindices, indices);
218   }
219   else {
220     int idx = this->whichToTraverse(action);
221     if (idx >= 0) {
222       this->children->traverse(action, idx);
223       PRIVATE(this)->enableTraversingOfInactiveChildren();
224       PRIVATE(this)->traverseInactiveChildren(this, action, idx, pathcode,
225                                               this->getNumChildren(),
226                                               this->getChildren());
227     }
228   }
229 }
230 
231 // Documented in superclass.
232 void
callback(SoCallbackAction * action)233 SoLOD::callback(SoCallbackAction *action)
234 {
235   SoLOD::doAction((SoAction*)action);
236 }
237 
238 // Documented in superclass.
239 void
audioRender(SoAudioRenderAction * action)240 SoLOD::audioRender(SoAudioRenderAction * action)
241 {
242   PRIVATE(this)->preAudioRender(this, action);
243   SoLOD::doAction((SoAction*)action);
244   PRIVATE(this)->postAudioRender(this, action);
245 }
246 
247 // Documented in superclass.
248 void
GLRender(SoGLRenderAction * action)249 SoLOD::GLRender(SoGLRenderAction * action)
250 {
251   switch (action->getCurPathCode()) {
252   case SoAction::NO_PATH:
253   case SoAction::BELOW_PATH:
254     SoLOD::GLRenderBelowPath(action);
255     break;
256   case SoAction::IN_PATH:
257     SoLOD::GLRenderInPath(action);
258     break;
259   case SoAction::OFF_PATH:
260     SoLOD::GLRenderOffPath(action);
261     break;
262   default:
263     assert(0 && "unknown path code.");
264     break;
265   }
266 }
267 
268 // Documented in superclass.
269 void
GLRenderBelowPath(SoGLRenderAction * action)270 SoLOD::GLRenderBelowPath(SoGLRenderAction * action)
271 {
272   int idx = this->whichToTraverse(action);
273   if (idx >= 0) {
274     SoNode * child = (SoNode*) this->children->get(idx);
275     action->pushCurPath(idx, child);
276     if (!action->abortNow()) {
277       SoNodeProfiling profiling;
278       profiling.preTraversal(action);
279       child->GLRenderBelowPath(action);
280       profiling.postTraversal(action);
281     }
282     action->popCurPath();
283   }
284   // don't auto cache LOD nodes.
285   SoGLCacheContextElement::shouldAutoCache(action->getState(),
286                                            SoGLCacheContextElement::DONT_AUTO_CACHE);
287 }
288 
289 // Documented in superclass.
290 void
GLRenderInPath(SoGLRenderAction * action)291 SoLOD::GLRenderInPath(SoGLRenderAction * action)
292 {
293   int numindices;
294   const int * indices;
295   SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
296 
297   if (pathcode == SoAction::IN_PATH) {
298     for (int i = 0; (i < numindices) && !action->hasTerminated(); i++) {
299       int idx = indices[i];
300       SoNode * node = this->getChild(idx);
301       action->pushCurPath(idx, node);
302       if (!action->abortNow()) {
303         SoNodeProfiling profiling;
304         profiling.preTraversal(action);
305         node->GLRenderInPath(action);
306         profiling.postTraversal(action);
307       }
308       action->popCurPath(pathcode);
309     }
310   }
311   else {
312     assert(pathcode == SoAction::BELOW_PATH);
313     SoLOD::GLRenderBelowPath(action);
314   }
315 }
316 
317 // Documented in superclass.
318 void
GLRenderOffPath(SoGLRenderAction * action)319 SoLOD::GLRenderOffPath(SoGLRenderAction * action)
320 {
321   int idx = this->whichToTraverse(action);;
322   if (idx >= 0) {
323     SoNode * node = this->getChild(idx);
324     if (node->affectsState()) {
325       action->pushCurPath(idx, node);
326       if (!action->abortNow()) {
327         SoNodeProfiling profiling;
328         profiling.preTraversal(action);
329         node->GLRenderOffPath(action);
330         profiling.postTraversal(action);
331       }
332       action->popCurPath();
333     }
334   }
335 }
336 
337 // Documented in superclass.
338 void
rayPick(SoRayPickAction * action)339 SoLOD::rayPick(SoRayPickAction *action)
340 {
341   SoLOD::doAction((SoAction*)action);
342 }
343 
344 // Documented in superclass.
345 void
getBoundingBox(SoGetBoundingBoxAction * action)346 SoLOD::getBoundingBox(SoGetBoundingBoxAction * action)
347 {
348   // FIXME: SGI OIV seems to do some extra work here, but the manual
349   // pages states that it should do a normal SoGroup traversal.
350   // we should _not_ use whichToTraverse() to calculate bbox as
351   // this would cause cache dependencies on the camera and
352   // the model matrix.                       pederb, 2001-02-21
353   inherited::getBoundingBox(action);
354 }
355 
356 // Documented in superclass.
357 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)358 SoLOD::getPrimitiveCount(SoGetPrimitiveCountAction *action)
359 {
360   SoLOD::doAction((SoAction*)action);
361 }
362 
363 /*!
364   Returns the child to traverse based on the ranges in
365   SoLOD::range. Will clamp to index to the number of children.  This
366   method will return -1 if no child should be traversed.  This will
367   only happen if the node has no children though.
368 */
369 int
whichToTraverse(SoAction * action)370 SoLOD::whichToTraverse(SoAction *action)
371 {
372   SoState *state = action->getState();
373   const SbMatrix &mat = SoModelMatrixElement::get(state);
374   const SbViewVolume &vv = SoViewVolumeElement::get(state);
375 
376   SbVec3f worldcenter;
377   mat.multVecMatrix(this->center.getValue(), worldcenter);
378 
379   float dist = (vv.getProjectionPoint() - worldcenter).length();
380 
381   int i;
382   int n = this->range.getNum();
383 
384   for (i = 0; i < n; i++) {
385     if (dist < this->range[i]) break;
386   }
387   if (i >= this->getNumChildren()) i = this->getNumChildren() - 1;
388   return i;
389 }
390 
391 // Doc from superclass.
392 void
notify(SoNotList * nl)393 SoLOD::notify(SoNotList * nl)
394 {
395   inherited::notify(nl);
396   PRIVATE(this)->notifyCalled();
397 }
398 
399 #undef PRIVATE
400 #undef PUBLIC
401