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