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 SoSwitch SoSwitch.h Inventor/nodes/SoSwitch.h
35 \brief The SoSwitch class is a group node which selects one child subgraph for traversal.
36
37 \ingroup nodes
38
39 Which child to traverse is controlled by the application programmer
40 by using the SoSwitch::whichChild field. In addition to picking out
41 a single child for traversal, it is also possible to flip all
42 children on or off for traversal.
43
44 This node is very useful for conditionally turning on or off parts
45 of the scenegraph based on the current application processing mode,
46 visualizing mode, or whatever else the application can do.
47
48 <b>FILE FORMAT/DEFAULTS:</b>
49 \code
50 Switch {
51 whichChild -1
52 }
53 \endcode
54 */
55
56 // *************************************************************************
57
58 #include <Inventor/nodes/SoSwitch.h>
59
60 #include <Inventor/misc/SoChildList.h>
61 #include <Inventor/actions/SoGetBoundingBoxAction.h>
62 #include <Inventor/actions/SoSearchAction.h>
63 #include <Inventor/actions/SoGetMatrixAction.h>
64 #include <Inventor/actions/SoGLRenderAction.h>
65 #include <Inventor/actions/SoPickAction.h>
66 #include <Inventor/actions/SoHandleEventAction.h>
67 #include <Inventor/actions/SoAudioRenderAction.h>
68 #include <Inventor/elements/SoSoundElement.h>
69 #include <Inventor/elements/SoSwitchElement.h>
70 #include <Inventor/actions/SoCallbackAction.h>
71 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
72 #include <Inventor/actions/SoWriteAction.h>
73 #include <Inventor/SoOutput.h>
74 #include <Inventor/errors/SoDebugError.h>
75
76 #include "nodes/SoSubNodeP.h"
77 #include "coindefs.h" // COIN_OBSOLETED()
78 #include "io/SoWriterefCounter.h"
79
80 // *************************************************************************
81
82 /*!
83 \var SoSFInt32 SoSwitch::whichChild
84
85 Selects which child to traverse during rendering (and some other)
86 actions.
87
88 The value should be either \c SO_SWITCH_NONE (for traversing no
89 children, like it was an empty SoGroup node), \c SO_SWITCH_ALL (for
90 traversing like if we were an SoGroup node), \c SO_SWITCH_INHERIT
91 (for traversing the same child as the last SoSwitch node), or an
92 index value for a child.
93
94 When using \c SO_SWITCH_INHERIT, it is important to understand how
95 the SoSwitch-node is affected by other SoSwitch-nodes. If you have
96 several switches in the scenegraph, the last switch with its \c
97 whichChild field set to anything but \c SO_SWITCH_INHERIT will be
98 used. The switch does not only inherit from its parent switch node,
99 but also from its siblings, located anywhere before it in the
100 scenegraph. An example will help clarify this:
101
102 \code
103 #Inventor V2.1 ascii
104
105 Separator {
106 Switch {
107 whichChild 0
108
109 Group {
110 Switch {
111 whichChild 1
112 BaseColor { rgb 1 0 0 } # red
113 BaseColor { rgb 1 1 0 } # yellow
114 }
115 Switch {
116 whichChild -2 # SO_SWITCH_INHERIT
117 BaseColor { rgb 0 1 0 } # green
118 BaseColor { rgb 0 0 1 } # blue
119 }
120 Cube { }
121 }
122 }
123 }
124 \endcode
125
126 This results in a blue cube on the screen. The reason being that the
127 value of the previous \c whichChild field was inherited by the final
128 switch, making it select child 1 - the blue BaseColor.
129
130 When constructing ascii Inventor files, the integer values for the
131 keywords must be used instead of their names. They are -1 for
132 \c SO_SWITCH_NONE, -2 for \c SO_SWITCH_INHERIT, and -3 for
133 \c SO_SWITCH_ALL.
134
135 Default value for the field is \c SO_SWITCH_NONE.
136 */
137
138 // *************************************************************************
139
140
141 #include "SoSoundElementHelper.h"
142
143 class SoSwitchP : public SoSoundElementHelper
144 {
145 public:
SoSwitchP(SoSwitch * master)146 SoSwitchP(SoSwitch * master) : master(master) {};
147 SoSwitch *master;
148 };
149
150 #define PRIVATE(p) ((p)->pimpl)
151 #define PUBLIC(p) ((p)->master)
152
153 SO_NODE_SOURCE(SoSwitch);
154
155 /*!
156 Default constructor.
157 */
SoSwitch(void)158 SoSwitch::SoSwitch(void)
159 {
160 this->commonConstructor();
161 }
162
163 /*!
164 Constructor.
165
166 The argument should be the approximate number of children which is
167 expected to be inserted below this node. The number need not be
168 exact, as it is only used as a hint for better memory resource
169 allocation.
170 */
SoSwitch(int numchildren)171 SoSwitch::SoSwitch(int numchildren)
172 : inherited(numchildren)
173 {
174 this->commonConstructor();
175 }
176
177 void
commonConstructor(void)178 SoSwitch::commonConstructor(void)
179 {
180 PRIVATE(this) = new SoSwitchP(this);
181
182 SO_NODE_INTERNAL_CONSTRUCTOR(SoSwitch);
183 SO_NODE_ADD_FIELD(whichChild, (SO_SWITCH_NONE));
184 }
185
186 /*!
187 Destructor.
188 */
~SoSwitch()189 SoSwitch::~SoSwitch()
190 {
191 delete PRIVATE(this);
192 }
193
194 // doc in super
195 void
initClass(void)196 SoSwitch::initClass(void)
197 {
198 SO_NODE_INTERNAL_INIT_CLASS(SoSwitch, SO_FROM_INVENTOR_1|SoNode::VRML1);
199
200 SO_ENABLE(SoGetBoundingBoxAction, SoSwitchElement);
201 SO_ENABLE(SoSearchAction, SoSwitchElement);
202 SO_ENABLE(SoGetMatrixAction, SoSwitchElement);
203 SO_ENABLE(SoGLRenderAction, SoSwitchElement);
204 SO_ENABLE(SoPickAction, SoSwitchElement);
205
206 SO_ENABLE(SoCallbackAction, SoSwitchElement);
207 SO_ENABLE(SoGetPrimitiveCountAction, SoSwitchElement);
208 SO_ENABLE(SoHandleEventAction, SoSwitchElement);
209 }
210
211 // Documented in superclass.
212 void
GLRender(SoGLRenderAction * action)213 SoSwitch::GLRender(SoGLRenderAction * action)
214 {
215 this->doAction(action);
216 }
217
218 // Documented in superclass.
219 void
getBoundingBox(SoGetBoundingBoxAction * action)220 SoSwitch::getBoundingBox(SoGetBoundingBoxAction * action)
221 {
222 SoSwitch::doAction(action);
223 }
224
225 // Documented in superclass.
226 void
search(SoSearchAction * action)227 SoSwitch::search(SoSearchAction * action)
228 {
229 // This method must be overridden in SoSwitch nodes to take into
230 // account if the search involves every single node, or just the
231 // nodes involved in normal graph traversal.
232
233 // Include this node in the search.
234 SoNode::search(action);
235 if (action->isFound()) return;
236
237 if (action->isSearchingAll()) {
238 this->children->traverse(action);
239 }
240 else {
241 SoSwitch::doAction(action);
242 }
243 }
244
245 // Documented in superclass.
246 void
doAction(SoAction * action)247 SoSwitch::doAction(SoAction * action)
248 {
249 SoState * state = action->getState();
250 int idx = this->whichChild.isIgnored() ?
251 SO_SWITCH_NONE : this->whichChild.getValue();
252 if (idx == SO_SWITCH_INHERIT) {
253 idx = SoSwitchElement::get(action->getState());
254 // when we inherit, idx might be out of range. Use modulo.
255 if (idx >= this->getNumChildren()) idx %= this->getNumChildren();
256 }
257 else {
258 SoSwitchElement::set(state, idx);
259 }
260
261 int numindices;
262 const int * indices;
263 SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
264
265 if (idx == SO_SWITCH_ALL ||
266 (action->isOfType(SoCallbackAction::getClassTypeId()) &&
267 ((SoCallbackAction *)action)->isCallbackAll())) {
268 if (action->isOfType(SoGetBoundingBoxAction::getClassTypeId())) {
269 SoGroup::getBoundingBox((SoGetBoundingBoxAction*) action);
270 }
271 else { // not a getBoundingBoxAction
272 if (pathcode == SoAction::IN_PATH) {
273 this->children->traverseInPath(action, numindices, indices);
274 }
275 else {
276 this->children->traverse(action);
277 }
278 }
279 }
280 else {
281 if (idx >= 0) { // should only traverse one child
282 if (pathcode == SoAction::IN_PATH) {
283 // traverse only if one path matches idx
284 for (int i = 0; i < numindices; i++) {
285 if (indices[i] == idx) {
286 this->children->traverse(action, idx);
287 break;
288 }
289 }
290 }
291 else { // off, below or no path traversal
292 // be robust for index out of range
293 if (idx >= this->getNumChildren()) {
294 #if COIN_DEBUG
295 static SbBool first = TRUE;
296 if (first) {
297 first = FALSE;
298 SbString s("(warning will be printed once, but there might be more cases of this problem).");
299 int lastidx = this->getNumChildren()-1;
300 if (lastidx >= 0) {
301 SoDebugError::post("SoSwitch::doAction",
302 "whichChild %d out of range [0, %d] %s",
303 idx, lastidx, s.getString());
304 }
305 else {
306 SoDebugError::post("SoSwitch::doAction",
307 "whichChild %d out of range -- "
308 "switch node has no children! %s",
309 idx, s.getString());
310 }
311 }
312 #endif // COIN_DEBUG
313 }
314 else {
315 this->children->traverse(action, idx);
316 }
317 }
318 }
319 PRIVATE(this)->traverseInactiveChildren(this, action, idx, pathcode,
320 this->getNumChildren(),
321 this->getChildren());
322 }
323 }
324
325 // Documented in superclass.
326 SbBool
affectsState(void) const327 SoSwitch::affectsState(void) const
328 {
329 // Overridden because when this function is called we don't know
330 // which "mode" the traversing action is in. If it's an
331 // SoSearchAction with isSearchingAll() set to TRUE, we should
332 // behave as if whichChild == SO_SWITCH_ALL, for instance.
333 //
334 // (To handle this exact case, SGI and TGS Inventor seems to use a
335 // global static flag SoSearchAction::duringSearchAll. We find this
336 // to be an utterly crap idea, though.)
337 //
338 // So to be safe, we _always_ behave as if whichChild is set to
339 // traverse all children. The worst that can happen is that we get a
340 // "false positive", ie TRUE when it should be FALSE. That means the
341 // action needs to traverse one level further down onto one of our
342 // children -- which will just take a miniscule amount of additional
343 // processing time.
344
345 int n = this->getNumChildren();
346 for (int i=0; i < n; i++) {
347 if (this->getChild(i)->affectsState()) { return TRUE; }
348 }
349 return FALSE;
350 }
351
352 // Documented in superclass.
353 void
callback(SoCallbackAction * action)354 SoSwitch::callback(SoCallbackAction *action)
355 {
356 SoSwitch::doAction(action);
357 }
358
359 // Documented in superclass.
360 void
audioRender(SoAudioRenderAction * action)361 SoSwitch::audioRender(SoAudioRenderAction * action)
362 {
363 PRIVATE(this)->preAudioRender(this, action);
364
365 SoSwitch::doAction((SoAction*)action);
366
367 PRIVATE(this)->postAudioRender(this, action);
368 }
369
370 // Documented in superclass.
371 void
pick(SoPickAction * action)372 SoSwitch::pick(SoPickAction *action)
373 {
374 SoSwitch::doAction((SoAction*)action);
375 }
376
377 // Documented in superclass.
378 void
handleEvent(SoHandleEventAction * action)379 SoSwitch::handleEvent(SoHandleEventAction *action)
380 {
381 SoSwitch::doAction(action);
382 }
383
384 // Documented in superclass.
385 void
getMatrix(SoGetMatrixAction * action)386 SoSwitch::getMatrix(SoGetMatrixAction *action)
387 {
388 switch (action->getCurPathCode()) {
389 case SoAction::OFF_PATH:
390 case SoAction::IN_PATH:
391 SoSwitch::doAction((SoAction*)action);
392 break;
393 default:
394 break;
395 }
396 }
397
398 // Documented in superclass.
399 void
write(SoWriteAction * action)400 SoSwitch::write(SoWriteAction * action)
401 {
402 // to keep child numbering, always write out all children for a
403 // switch
404
405 SoOutput * out = action->getOutput();
406 if (out->getStage() == SoOutput::COUNT_REFS) {
407 this->addWriteReference(out, FALSE);
408 // No need to traverse children as SoGroup::addWriteReference() already
409 // handles write references of children.
410 }
411 else if (out->getStage() == SoOutput::WRITE) {
412 if (this->writeHeader(out, TRUE, FALSE)) return;
413 this->getFieldData()->write(out, this);
414 if (out->isBinary()) out->write(this->getNumChildren());
415 this->getChildren()->traverse(action);
416 this->writeFooter(out);
417 }
418 else assert(0 && "unknown stage");
419 }
420
421 // Documented in superclass.
422 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)423 SoSwitch::getPrimitiveCount(SoGetPrimitiveCountAction *action)
424 {
425 SoSwitch::doAction((SoAction*)action);
426 }
427
428 /*!
429 This function was part of the original SGI Inventor API, but it is
430 not supported in Coin, as it looks like it should probably have been
431 private in Inventor.
432 */
433 void
traverseChildren(SoAction * COIN_UNUSED_ARG (action))434 SoSwitch::traverseChildren(SoAction * COIN_UNUSED_ARG(action))
435 {
436 COIN_OBSOLETED();
437 }
438
439 // Doc from superclass.
440 void
notify(SoNotList * nl)441 SoSwitch::notify(SoNotList * nl)
442 {
443 SoNotRec * rec = nl->getLastRec();
444
445 // If whichChild is set to a specific child and we get a
446 // notification from some other child, ignore it to avoid redraws
447 // and invalidated caches because of inactive parts of the scene
448 // graph. This fixes cases like these:
449
450 // DEF Switch {
451 // whichChild 1
452 // Separator {
453 // Rotor {
454 // on TRUE
455 // speed 1
456 // }
457 // }
458 // Separator {
459 // Cube { }
460 // }
461 // }
462
463 SbBool ignoreit = FALSE;
464
465 // if getBase() == this, the notification is from a field under this
466 // node, and should _not_ be ignored
467 if (rec && (rec->getBase() != (SoBase*) this)) {
468 int which = this->whichChild.getValue();
469 if (which == -1) ignoreit = TRUE; // also ignore if no children are traversed
470 else if (which >= 0) {
471 int fromchild = this->findChild((SoNode*) rec->getBase());
472 if (fromchild >= 0 && fromchild != which) ignoreit = TRUE;
473 }
474 }
475
476 if (!ignoreit) {
477 inherited::notify(nl);
478 PRIVATE(this)->notifyCalled();
479 }
480 }
481
482 #undef PRIVATE
483 #undef PUBLIC
484