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 SoSeparator SoSeparator.h Inventor/nodes/SoSeparator.h
35 \brief The SoSeparator class is a state-preserving group node.
36
37 \ingroup nodes
38
39 Subgraphs parented by SoSeparator nodes will not affect the state,
40 as they push and pop the traversal state before and after traversal
41 of its children.
42
43 SoSeparator nodes also provides options for traversal optimalization
44 through the use of caching.
45
46 <b>FILE FORMAT/DEFAULTS:</b>
47 \code
48 Separator {
49 renderCaching AUTO
50 boundingBoxCaching AUTO
51 renderCulling AUTO
52 pickCulling AUTO
53 }
54 \endcode
55
56 \sa SoTransformSeparator
57 */
58
59 // *************************************************************************
60
61 #include <Inventor/nodes/SoSeparator.h>
62
63 #ifdef HAVE_CONFIG_H
64 #include "config.h"
65 #endif // HAVE_CONFIG_H
66
67 #include <cstdlib> // strtol(), rand()
68 #include <climits> // LONG_MIN, LONG_MAX
69
70 #include <Inventor/actions/SoCallbackAction.h>
71 #include <Inventor/actions/SoGLRenderAction.h>
72 #include <Inventor/actions/SoGetBoundingBoxAction.h>
73 #include <Inventor/actions/SoGetMatrixAction.h>
74 #include <Inventor/actions/SoHandleEventAction.h>
75 #include <Inventor/actions/SoRayPickAction.h>
76 #include <Inventor/actions/SoSearchAction.h>
77 #include <Inventor/actions/SoAudioRenderAction.h>
78 #include <Inventor/caches/SoBoundingBoxCache.h>
79 #include <Inventor/caches/SoGLCacheList.h>
80 #include <Inventor/elements/SoCacheElement.h>
81 #include <Inventor/elements/SoCullElement.h>
82 #include <Inventor/elements/SoLocalBBoxMatrixElement.h>
83 #include <Inventor/elements/SoSoundElement.h>
84 #include <Inventor/misc/SoChildList.h>
85 #include <Inventor/misc/SoState.h>
86 #include <Inventor/errors/SoDebugError.h>
87 #include <Inventor/system/gl.h>
88 #include <Inventor/C/tidbits.h> // coin_getenv()
89 #include <Inventor/threads/SbStorage.h>
90
91 #ifdef COIN_THREADSAFE
92 #include <Inventor/threads/SbMutex.h>
93 #endif // COIN_THREADSAFE
94
95 #include "coindefs.h" // COIN_OBSOLETED()
96 #include "nodes/SoSubNodeP.h"
97 #include "glue/glp.h"
98 #include "rendering/SoGL.h"
99 #include "misc/SoDBP.h"
100
101 #include <Inventor/annex/Profiler/SoProfiler.h>
102 #include "profiler/SoNodeProfiling.h"
103
104 // *************************************************************************
105
106 #if COIN_DEBUG
107 #define GLCACHE_DEBUG 0 // set to 1 to debug caching
108 #endif
109
110 // *************************************************************************
111
112 // environment variable
113 static int COIN_RANDOMIZE_RENDER_CACHING = -1;
114
115 // Maximum number of caches available for allocation for the
116 // rendercaching.
117 int SoSeparator::numrendercaches = 2;
118
119 // *************************************************************************
120
121 /*!
122 \enum SoSeparator::CacheEnabled
123
124 Enumeration of flags for the fields deciding which optimalizations
125 to do in SoSeparator nodes. There are two types of settings
126 available: caching policies or culling policies. See doumentation of
127 fields.
128 */
129 /*!
130 \var SoSeparator::CacheEnabled SoSeparator::OFF
131 No caching.
132 */
133 /*!
134 \var SoSeparator::CacheEnabled SoSeparator::ON
135 Always try to cache state.
136 */
137 /*!
138 \var SoSeparator::CacheEnabled SoSeparator::AUTO
139 Use heuristics to try to figure out the optimal caching policy.
140 */
141
142
143 /*!
144 \var SoSFEnum SoSeparator::renderCaching
145
146 Policy for caching of rendering instructions for faster
147 execution. This will typically use the OpenGL \e displaylist
148 mechanism.
149
150 Default value is SoSeparator::AUTO.
151
152 If you know that some parts of your scene will never change,
153 rendering might happen faster if you explicitly set this field to
154 SoSeparator::ON. If you on the other hand know that parts of the
155 scene will change a lot (like for every redraw), it will be
156 beneficial to set this field to SoSeparator::OFF for the top-level
157 separator node of this (sub)graph.
158
159 Usually the default setting of \c AUTO will handle any scene very
160 well. The advantages that \e can be had from setting
161 SoSeparator::renderCaching to \c ON are:
162
163 <ul>
164 <li> If you positively know that the geometry under the SoSeparator is
165 static, you get the cache set up right away.
166
167 Otherwise, the code in Coin will do a bit of testing and decide
168 by some heuristics whether or not to enable it. That will make
169 the rendering be a tiny bit slower right after start-up than with
170 renderCaching set to \c ON.
171
172 (The slow-down should hardly be noticable, though, so we don't
173 advice application programmers to do this.)
174 </li>
175
176 <li> For many of the shape nodes that can contain many basic
177 primitives, like e.g. SoFaceSet, SoIndexedFaceSet, SoLineSet, etc
178 etc, there is an internal threshold for how many primitives a
179 node can contain before we don't do caching when
180 SoSeparator::renderCaching is set to \c AUTO.
181
182 The reason we do this is because OpenGL renderlists can
183 potentially suck up a lot of memory resources on the graphics
184 card.
185
186 But if you know that it will be advantageous on your particular
187 platform, you can override this by setting
188 SoSeparator::renderCaching equal to \c ON.
189
190 (We don't advice application programmers to do this either. A
191 better solution in these cases would simply be to get in touch
192 with SIM and describe the platform and the problem, and we could
193 integrate a proper fix into Coin.)
194 </li>
195 </ul>
196
197 There are good reasons for setting renderCaching to \c OFF, like
198 when you know the geometry will be changing a lot. Still, Coin
199 should work fairly well without even this optimization. (If
200 renderCaching is \c AUTO over a sub-graph with changing geometry or
201 other cache smashing nodes, the caching heuristics will stop the
202 SoSeparator node from trying to make caches -- at least after a few
203 tries has been made and failed.)
204
205
206 The short story about how auto-caching works is as follows:
207
208 <ul>
209
210 <li>For vertex-based shapes with fewer than 100 triangles and
211 where the geometry is detected to be fairly static, caching is
212 enabled.</li>
213
214 <li>For shapes with more than 1000 trangles, it is disabled, to
215 avoid spending too much of the on-board graphics card's memory
216 resources.</li>
217
218 <li>For shapes with between 100 and 1000 shapes, displaylist
219 caching will be turned on if our heuristics decides that the
220 geometry can be considered static.</li>
221
222 </ul>
223
224 The maximum threshold (of 1000) is higher when doing remote
225 rendering (as when rendering from one X11-based system to another).
226
227 Disabling the displaylist caching takes precedence over enabling, so
228 if you have an SoSeparator with a shape with more than 1000
229 triangles and a shape with fewer than 100 triangles, caching will be
230 disabled for the SoSeparator.
231
232 It's possible to tune the limits using some environment variables:
233
234 <ul>
235
236 <li>\c COIN_AUTOCACHE_LOCAL_MIN can be used to change the
237 enable-caching limit, while \c COIN_AUTOCACHE_LOCAL_MAX
238 controls the disable-caching limit.</li>
239
240 <li>The corresponding variables for remote rendering are \c
241 COIN_AUTOCACHE_REMOTE_MIN and \c
242 COIN_AUTOCACHE_REMOTE_MAX.</li>
243
244 </ul>
245 */
246
247 /*!
248 \var SoSFEnum SoSeparator::boundingBoxCaching
249
250 Policy for caching bounding box calculations. Default value is
251 SoSeparator::AUTO.
252
253 See also documentation for SoSeparator::renderCaching.
254 */
255 /*!
256 \var SoSFEnum SoSeparator::renderCulling
257
258 Policy for doing viewport culling during rendering
259 traversals. Default value is SoSeparator::AUTO.
260
261 When the render culling is turned off for Coin, it will be left to
262 be done for the underlying immediate mode rendering library. This
263 will often be faster than doing culling from within Coin, so be
264 careful to monitor the change in execution speed if setting this
265 field to SoSeparator::ON.
266
267 See also documentation for SoSeparator::renderCaching.
268 */
269 /*!
270 \var SoSFEnum SoSeparator::pickCulling
271
272 Policy for doing viewport culling during pick traversals. Default
273 value is SoSeparator::AUTO.
274
275 See documentation for SoSeparator::renderCulling.
276 */
277
278 // *************************************************************************
279
280 // when doing threadsafe rendering, each thread needs its own
281 // glcachelist
282 typedef struct {
283 SoGLCacheList * glcachelist;
284 } soseparator_storage;
285
286 static void
soseparator_storage_construct(void * data)287 soseparator_storage_construct(void * data)
288 {
289 soseparator_storage * ptr = (soseparator_storage*) data;
290 ptr->glcachelist = NULL;
291 }
292
293 static void
soseparator_storage_destruct(void * data)294 soseparator_storage_destruct(void * data)
295 {
296 soseparator_storage * ptr = (soseparator_storage*) data;
297 delete ptr->glcachelist;
298 }
299
300 // *************************************************************************
301
302 class SoSeparatorP {
303 public:
SoSeparatorP(void)304 SoSeparatorP(void) {
305 this->glcachestorage =
306 new SbStorage(sizeof(soseparator_storage),
307 soseparator_storage_construct,
308 soseparator_storage_destruct);
309 this->pub = NULL;
310 }
~SoSeparatorP()311 ~SoSeparatorP() {
312 delete this->glcachestorage;
313 }
314
315 SoSeparator * pub;
316
317 SoBoundingBoxCache * bboxcache;
318 uint32_t bboxcache_usecount;
319 uint32_t bboxcache_destroycount;
320
321 #ifdef COIN_THREADSAFE
322 // FIXME: a mutex for every SoSeparator instance seems a bit
323 // excessive, especially since MSWindows might have rather strict
324 // limits on the total amount of mutex resources a process (or even
325 // a user) can allocate. so consider making this a class-wide
326 // instance instead. -mortene.
327 SbMutex mutex;
328 #endif // !COIN_THREADSAFE
329 SbStorage * glcachestorage;
invalidate_gl_cache(void * tls,void *)330 static void invalidate_gl_cache(void * tls, void *) {
331 soseparator_storage * ptr = (soseparator_storage*) tls;
332 if (ptr->glcachelist) {
333 ptr->glcachelist->invalidateAll();
334 }
335 }
336
337 enum { YES, NO, MAYBE } hassoundchild;
338
339 SoGLCacheList * getGLCacheList(SbBool createifnull);
340
invalidateGLCaches(void)341 void invalidateGLCaches(void) {
342 glcachestorage->applyToAll(invalidate_gl_cache, NULL);
343 }
344
lock(void)345 void lock(void) {
346 #ifdef COIN_THREADSAFE
347 this->mutex.lock();
348 #endif // COIN_THREADSAFE
349 }
unlock(void)350 void unlock(void) {
351 #ifdef COIN_THREADSAFE
352 this->mutex.unlock();
353 #endif // COIN_THREADSAFE
354 }
355
356 static SbBool doCull(SoSeparatorP * thisp, SoState * state,
357 SbBool (* cullfunc)(SoState *, const SbBox3f &, const SbBool));
358 };
359
360 #define PRIVATE(obj) ((obj)->pimpl)
361 #define PUBLIC(obj) ((obj)->pub)
362
363 // *************************************************************************
364
365 SoGLCacheList *
getGLCacheList(SbBool createifnull)366 SoSeparatorP::getGLCacheList(SbBool createifnull)
367 {
368 soseparator_storage * ptr =
369 (soseparator_storage*) this->glcachestorage->get();
370 if (createifnull && ptr->glcachelist == NULL) {
371 ptr->glcachelist = new SoGLCacheList(SoSeparator::getNumRenderCaches());
372 }
373 return ptr->glcachelist;
374 }
375
376 // *************************************************************************
377
378 SO_NODE_SOURCE(SoSeparator);
379
380 /*!
381 Default constructor.
382 */
SoSeparator(void)383 SoSeparator::SoSeparator(void)
384 {
385 this->commonConstructor();
386 }
387
388 /*!
389 Constructor.
390
391 The argument should be the approximate number of children which is
392 expected to be inserted below this node. The number need not be
393 exact, as it is only used as a hint for better memory resource
394 allocation.
395 */
SoSeparator(const int nchildren)396 SoSeparator::SoSeparator(const int nchildren)
397 : SoGroup(nchildren)
398 {
399 this->commonConstructor();
400 }
401
402 // private common constructor helper function
403 void
commonConstructor(void)404 SoSeparator::commonConstructor(void)
405 {
406 PRIVATE(this)->pub = this;
407
408 SO_NODE_INTERNAL_CONSTRUCTOR(SoSeparator);
409
410 SO_NODE_ADD_FIELD(renderCaching, (SoSeparator::AUTO));
411 SO_NODE_ADD_FIELD(boundingBoxCaching, (SoSeparator::AUTO));
412 SO_NODE_ADD_FIELD(renderCulling, (SoSeparator::AUTO));
413 SO_NODE_ADD_FIELD(pickCulling, (SoSeparator::AUTO));
414
415 SO_NODE_DEFINE_ENUM_VALUE(CacheEnabled, ON);
416 SO_NODE_DEFINE_ENUM_VALUE(CacheEnabled, OFF);
417 SO_NODE_DEFINE_ENUM_VALUE(CacheEnabled, AUTO);
418
419 SO_NODE_SET_SF_ENUM_TYPE(renderCaching, CacheEnabled);
420 SO_NODE_SET_SF_ENUM_TYPE(boundingBoxCaching, CacheEnabled);
421 SO_NODE_SET_SF_ENUM_TYPE(renderCulling, CacheEnabled);
422 SO_NODE_SET_SF_ENUM_TYPE(pickCulling, CacheEnabled);
423
424 static long int maxcaches = -1;
425 if (maxcaches == -1) {
426 maxcaches = -2; // so we don't request the envvar later if it is not set
427 const char * maxcachesstr = coin_getenv("IV_SEPARATOR_MAX_CACHES");
428 if (maxcachesstr) {
429 maxcaches = strtol(maxcachesstr, NULL, 10);
430 if ((maxcaches == LONG_MIN) || (maxcaches == LONG_MAX) || (maxcaches < 0)) {
431 SoDebugError::post("SoSeparator::commonConstructor",
432 "Environment variable IV_SEPARATOR_MAX_CACHES "
433 "has invalid setting \"%s\"", maxcachesstr);
434 }
435 else {
436 SoSeparator::setNumRenderCaches(maxcaches);
437 }
438 }
439 }
440
441 PRIVATE(this)->bboxcache = NULL;
442 PRIVATE(this)->bboxcache_usecount = 0;
443 PRIVATE(this)->bboxcache_destroycount = 0;
444
445 // This environment variable used for local stability / robustness /
446 // correctness testing of the render caching. If set >= 1,
447 // renderCaching will be set to "ON" with a probability of 0.5 for
448 // every SoSeparator instantiated.
449 if (COIN_RANDOMIZE_RENDER_CACHING < 0) {
450 const char * env = coin_getenv("COIN_RANDOMIZE_RENDER_CACHING");
451 if (env) COIN_RANDOMIZE_RENDER_CACHING = atoi(env);
452 else COIN_RANDOMIZE_RENDER_CACHING = 0;
453 }
454 if (COIN_RANDOMIZE_RENDER_CACHING > 0) {
455 if (rand() > (RAND_MAX/2)) { this->renderCaching = SoSeparator::ON; }
456 }
457
458 PRIVATE(this)->hassoundchild = SoSeparatorP::MAYBE;
459 }
460
461 /*!
462 Destructor. Frees resources used to implement caches.
463 */
~SoSeparator()464 SoSeparator::~SoSeparator()
465 {
466 if (PRIVATE(this)->bboxcache) {
467 PRIVATE(this)->bboxcache->unref();
468 }
469 }
470
471 // Doc from superclass.
472 void
initClass(void)473 SoSeparator::initClass(void)
474 {
475 SO_NODE_INTERNAL_INIT_CLASS(SoSeparator, SO_FROM_INVENTOR_1|SoNode::VRML1);
476
477 SO_ENABLE(SoGetBoundingBoxAction, SoCacheElement);
478 SO_ENABLE(SoGLRenderAction, SoCacheElement);
479 SoSeparator::numrendercaches = 2;
480 }
481
482 // Doc from superclass.
483 void
doAction(SoAction * action)484 SoSeparator::doAction(SoAction * action)
485 {
486 action->getState()->push();
487 inherited::doAction(action);
488 action->getState()->pop();
489 }
490
491 // Doc from superclass.
492 void
getBoundingBox(SoGetBoundingBoxAction * action)493 SoSeparator::getBoundingBox(SoGetBoundingBoxAction * action)
494 {
495 SoState * state = action->getState();
496
497 SbXfBox3f childrenbbox;
498 SbBool childrencenterset;
499 SbVec3f childrencenter;
500
501 // FIXME: AUTO is interpreted as ON for the boundingBoxCaching
502 // field, but we should trigger some heuristics based on scene graph
503 // "behavior" in the children subgraphs if the value is set to
504 // AUTO. 19990513 mortene.
505 SbBool iscaching = this->boundingBoxCaching.getValue() != OFF;
506
507 switch (action->getCurPathCode()) {
508 case SoAction::IN_PATH:
509 // can't cache if we're not traversing all children
510 iscaching = FALSE;
511 break;
512 case SoAction::OFF_PATH:
513 return; // no need to do any more work
514 case SoAction::BELOW_PATH:
515 case SoAction::NO_PATH:
516 // check if this is a normal traversal
517 if (action->isInCameraSpace() || action->isResetPath()) iscaching = FALSE;
518 break;
519 default:
520 iscaching = FALSE;
521 assert(0 && "unknown path code");
522 break;
523 }
524
525 SbBool validcache = iscaching && PRIVATE(this)->bboxcache && PRIVATE(this)->bboxcache->isValid(state);
526
527 if (iscaching && validcache) {
528 SoCacheElement::addCacheDependency(state, PRIVATE(this)->bboxcache);
529 PRIVATE(this)->bboxcache_usecount++;
530 childrenbbox = PRIVATE(this)->bboxcache->getBox();
531 childrencenterset = PRIVATE(this)->bboxcache->isCenterSet();
532 childrencenter = PRIVATE(this)->bboxcache->getCenter();
533 if (PRIVATE(this)->bboxcache->hasLinesOrPoints()) {
534 SoBoundingBoxCache::setHasLinesOrPoints(state);
535 }
536 }
537 else {
538 SbXfBox3f abox = action->getXfBoundingBox();
539
540 SbBool storedinvalid = FALSE;
541
542 // check if we should disable auto caching
543 if (PRIVATE(this)->bboxcache_destroycount > 10 && this->boundingBoxCaching.getValue() == AUTO) {
544 if (float(PRIVATE(this)->bboxcache_usecount) / float(PRIVATE(this)->bboxcache_destroycount) < 5.0f) {
545 iscaching = FALSE;
546 }
547 }
548
549 if (iscaching) {
550 storedinvalid = SoCacheElement::setInvalid(FALSE);
551 }
552 state->push();
553
554 if (iscaching) {
555 // lock before changing the bboxcache pointer so that the notify()
556 // function can be used by another thread.
557 PRIVATE(this)->lock();
558 // if we get here, we know bbox cache is not created or is invalid
559 if (PRIVATE(this)->bboxcache) {
560 PRIVATE(this)->bboxcache_destroycount++;
561 PRIVATE(this)->bboxcache->unref();
562 }
563 PRIVATE(this)->bboxcache = new SoBoundingBoxCache(state);
564 PRIVATE(this)->bboxcache->ref();
565 PRIVATE(this)->unlock();
566 // set active cache to record cache dependencies
567 SoCacheElement::set(state, PRIVATE(this)->bboxcache);
568 }
569
570 SoLocalBBoxMatrixElement::makeIdentity(state);
571 action->getXfBoundingBox().makeEmpty();
572 inherited::getBoundingBox(action);
573
574 childrenbbox = action->getXfBoundingBox();
575 childrencenterset = action->isCenterSet();
576 if (childrencenterset) childrencenter = action->getCenter();
577
578 action->getXfBoundingBox() = abox; // reset action bbox
579
580 if (iscaching) {
581 PRIVATE(this)->bboxcache->set(childrenbbox, childrencenterset, childrencenter);
582 }
583 state->pop();
584 if (iscaching) SoCacheElement::setInvalid(storedinvalid);
585 }
586
587 if (!childrenbbox.isEmpty()) {
588 action->extendBy(childrenbbox);
589 if (childrencenterset) {
590 // FIXME: shouldn't this assert() hold up? Investigate. 19990422 mortene.
591 #if 0 // disabled
592 assert(!action->isCenterSet());
593 #else
594 action->resetCenter();
595 #endif
596 action->setCenter(childrencenter, TRUE);
597 }
598 }
599 }
600
601 // Doc from superclass.
602 void
callback(SoCallbackAction * action)603 SoSeparator::callback(SoCallbackAction * action)
604 {
605 SoState * state = action->getState();
606 state->push();
607 // culling planes should normally not be set, but can be set
608 // manually by the application programmer to optimize callback
609 // action traversal.
610
611 if (!this->cullTest(state)) {
612 SoGroup::callback(action);
613 }
614 state->pop();
615 }
616
617 // *************************************************************************
618
619 // Doc from superclass.
620 void
GLRender(SoGLRenderAction * action)621 SoSeparator::GLRender(SoGLRenderAction * action)
622 {
623 switch (action->getCurPathCode()) {
624 case SoAction::NO_PATH:
625 case SoAction::BELOW_PATH:
626 this->GLRenderBelowPath(action);
627 break;
628 case SoAction::OFF_PATH:
629 // do nothing. Separator will reset state.
630 break;
631 case SoAction::IN_PATH:
632 this->GLRenderInPath(action);
633 break;
634 }
635 }
636
637 /*!
638 SGI Open Inventor v2.1 obsoleted support for
639 SoGLRenderAction::addMethod(). Instead, GLRender() might be called
640 directly, and to optimize traversal, the SoSeparator node calls
641 GLRenderBelowPath whenever the path code is BELOW_PATH or NO_PATH
642 (path code is guaranteed not to change). To be compatible with SGI's
643 Inventor (and thereby also TGS') we have chosen to follow their
644 implementation in this respect.
645
646 SoSeparator::GLRenderBelowPath() do not traverse its children using
647 SoChildList::traverse(), but calls GLRenderBelowPath() directly
648 for all its children.
649 */
650 void
GLRenderBelowPath(SoGLRenderAction * action)651 SoSeparator::GLRenderBelowPath(SoGLRenderAction * action)
652 {
653 SoState * state = action->getState();
654 state->push();
655 SbBool didcull = FALSE;
656
657 SoGLCacheList * createcache = NULL;
658 if ((this->renderCaching.getValue() != OFF) &&
659 (SoSeparator::getNumRenderCaches() > 0)) {
660
661 // test if bbox is outside view-volume
662 if (!state->isCacheOpen()) {
663 didcull = TRUE;
664 if (this->cullTest(state)) {
665 state->pop();
666 return;
667 }
668 }
669 PRIVATE(this)->lock();
670 SoGLCacheList * glcachelist = PRIVATE(this)->getGLCacheList(TRUE);
671 PRIVATE(this)->unlock();
672 if (glcachelist->call(action)) {
673 #if GLCACHE_DEBUG // debug
674 SoDebugError::postInfo("SoSeparator::GLRenderBelowPath",
675 "%p executed GL cache", this);
676 #endif // debug
677 state->pop();
678
679 if (SoProfiler::isEnabled()) {
680 SoProfilerElement * e = SoProfilerElement::get(state);
681 if (e) {
682 e->getProfilingData().setNodeFlag(action->getCurPath(), SbProfilingData::GL_CACHED_FLAG, TRUE);
683 }
684 }
685
686 return;
687 }
688
689 if (!SoCacheElement::anyOpen(state)) {
690 #if GLCACHE_DEBUG // debug
691 SoDebugError::postInfo("SoSeparator::GLRenderBelowPath",
692 "%p creating GL cache", this);
693 #endif // debug
694 createcache = glcachelist;
695 }
696 }
697
698 if (createcache) {
699 createcache->open(action, this->renderCaching.getValue() == AUTO);
700 }
701
702 SbBool outsidefrustum =
703 (createcache || state->isCacheOpen() || didcull) ?
704 FALSE : this->cullTest(state);
705 if (createcache || !outsidefrustum) {
706 int n = this->children->getLength();
707 SoNode ** childarray = (n!=0)? reinterpret_cast<SoNode**>(this->children->getArrayPtr()) : NULL;
708 action->pushCurPath();
709 for (int i = 0; i < n && !action->hasTerminated(); i++) {
710 action->popPushCurPath(i, childarray[i]);
711 if (action->abortNow()) {
712 // only cache if we do a full traversal
713 SoCacheElement::invalidate(state);
714 break;
715 }
716
717 {
718 SoNodeProfiling profiling;
719 profiling.preTraversal(action);
720 childarray[i]->GLRenderBelowPath(action); // traversal call
721 profiling.postTraversal(action);
722 }
723
724 #if COIN_DEBUG
725 // The GL error test is default disabled for this optimized
726 // path. If you get a GL error reporting an error in the
727 // Separator node, enable this code by setting the environment
728 // variable COIN_GLERROR_DEBUGGING to "1" to see exactly which
729 // node caused the error.
730 static SbBool chkglerr = sogl_glerror_debugging();
731 if (chkglerr) {
732 cc_string str;
733 cc_string_construct(&str);
734 const unsigned int errs = coin_catch_gl_errors(&str);
735 if (errs > 0) {
736 SoDebugError::post("SoSeparator::GLRenderBelowPath",
737 "GL error: '%s', nodetype: %s",
738 cc_string_get_text(&str),
739 (*this->children)[i]->getTypeId().getName().getString());
740 }
741 cc_string_clean(&str);
742 }
743 #endif // COIN_DEBUG
744 }
745 action->popCurPath();
746 }
747 state->pop();
748 if (createcache) {
749 createcache->close(action);
750 }
751 }
752
753 // Doc from superclass.
754 void
GLRenderInPath(SoGLRenderAction * action)755 SoSeparator::GLRenderInPath(SoGLRenderAction * action)
756 {
757 int numindices;
758 const int * indices;
759
760 SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
761
762 if (pathcode == SoAction::IN_PATH) {
763 SoState * state = action->getState();
764 SoNode ** childarray = (SoNode**) this->children->getArrayPtr();
765 state->push();
766 int childidx = 0;
767 for (int i = 0; i < numindices; i++) {
768 for (; childidx < indices[i] && !action->hasTerminated(); childidx++) {
769 SoNode * offpath = childarray[childidx];
770 if (offpath->affectsState()) {
771 action->pushCurPath(childidx, offpath);
772 if (!action->abortNow()) {
773 SoNodeProfiling profiling;
774 profiling.preTraversal(action);
775 offpath->GLRenderOffPath(action); // traversal call
776 profiling.postTraversal(action);
777 }
778 else {
779 SoCacheElement::invalidate(state);
780 }
781 action->popCurPath(pathcode);
782 }
783 }
784 SoNode * inpath = childarray[childidx];
785 action->pushCurPath(childidx, inpath);
786 if (!action->abortNow()) {
787 SoNodeProfiling profiling;
788 profiling.preTraversal(action);
789 inpath->GLRenderInPath(action); // traversal call
790 profiling.postTraversal(action);
791 }
792 else {
793 SoCacheElement::invalidate(state);
794 }
795 action->popCurPath(pathcode);
796 childidx++;
797 }
798 state->pop();
799 }
800 else if (pathcode == SoAction::BELOW_PATH) {
801 this->GLRenderBelowPath(action);
802 }
803 }
804
805 // Doc from superclass.
806 void
GLRenderOffPath(SoGLRenderAction *)807 SoSeparator::GLRenderOffPath(SoGLRenderAction *)
808 {
809 // do nothing, since all state changes will be reset by the separator
810 }
811
812 // Doc from superclass.
813 void
handleEvent(SoHandleEventAction * action)814 SoSeparator::handleEvent(SoHandleEventAction * action)
815 {
816 SoSeparator::doAction(action);
817 }
818
819 // Doc from superclass.
820 void
audioRender(SoAudioRenderAction * action)821 SoSeparator::audioRender(SoAudioRenderAction * action)
822 {
823 // Note: This function is similar to SoVRMLGroup::audioRender().
824 /* FIXME: how should we handle termination of an action? We should
825 probably reset PRIVATE(this)->hassoundchild to MAYBE. Investigate
826 2003-01-31 thammer. */
827
828 int numindices;
829 const int * indices;
830 SoState * state = action->getState();
831 if (PRIVATE(this)->hassoundchild != SoSeparatorP::NO) {
832 if (action->getPathCode(numindices, indices) != SoAction::IN_PATH) {
833 action->getState()->push();
834 SoSoundElement::setSceneGraphHasSoundNode(state, this, FALSE);
835 inherited::doAction(action);
836 PRIVATE(this)->hassoundchild = SoSoundElement::sceneGraphHasSoundNode(state) ?
837 SoSeparatorP::YES : SoSeparatorP::NO;
838 action->getState()->pop();
839 } else {
840 SoSeparator::doAction((SoAction*)action);
841 }
842 }
843 }
844
845 // compute object space ray and test for intersection
846 static SbBool
ray_intersect(SoRayPickAction * action,const SbBox3f & box)847 ray_intersect(SoRayPickAction * action, const SbBox3f &box)
848 {
849 if (box.isEmpty()) return FALSE;
850 action->setObjectSpace();
851 return action->intersect(box, TRUE);
852 }
853
854 // Doc from superclass.
855 void
rayPick(SoRayPickAction * action)856 SoSeparator::rayPick(SoRayPickAction * action)
857 {
858 if (this->pickCulling.getValue() == OFF ||
859 !PRIVATE(this)->bboxcache || !PRIVATE(this)->bboxcache->isValid(action->getState()) ||
860 !action->hasWorldSpaceRay() ||
861 ray_intersect(action, PRIVATE(this)->bboxcache->getProjectedBox())) {
862 SoSeparator::doAction(action);
863 }
864 }
865
866 // Doc from superclass.
867 void
search(SoSearchAction * action)868 SoSeparator::search(SoSearchAction * action)
869 {
870 // Include this node in the search. (_Don't_ use the
871 // SoGroup::search(), as it will also traverse all children.)
872 SoNode::search(action);
873
874 if (action->isFound()) return;
875
876 SoSeparator::doAction(action);
877 }
878
879 // Doc from superclass.
880 void
getMatrix(SoGetMatrixAction * action)881 SoSeparator::getMatrix(SoGetMatrixAction * action)
882 {
883 int numindices;
884 const int * indices;
885 if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) {
886 // need to push/pop to handle SoUnitsElement correctly
887 action->getState()->push();
888 this->children->traverseInPath(action, numindices, indices);
889 action->getState()->pop();
890 }
891 }
892
893 /*!
894 Set the maximum number of caches that SoSeparator nodes may allocate
895 for render caching.
896
897 This is a global value which will be used for all SoSeparator nodes,
898 but the value indicate the maximum number \e per SoSeparator node.
899
900 More caches might give better performance, but will use more memory.
901 The built-in default value is 2.
902
903 The value can also be changed globally by setting the host system's
904 environment variable IV_SEPARATOR_MAX_CACHES to the wanted
905 number. This is primarily meant as an aid during debugging, to make
906 it easy to turn off rendercaching completely (by setting
907 "IV_SEPARATOR_MAX_CACHES=0") without having to change any
908 application code.
909 */
910 void
setNumRenderCaches(const int howmany)911 SoSeparator::setNumRenderCaches(const int howmany)
912 {
913 SoSeparator::numrendercaches = howmany;
914 }
915
916 /*!
917 Returns maximum number of caches SoSeparator nodes are allowed to
918 use for render caching.
919
920 \sa setNumRenderCaches()
921 */
922 int
getNumRenderCaches(void)923 SoSeparator::getNumRenderCaches(void)
924 {
925 return SoSeparator::numrendercaches;
926 }
927
928 // Doc from superclass.
929 SbBool
affectsState(void) const930 SoSeparator::affectsState(void) const
931 {
932 // Subgraphs parented by SoSeparator nodes will not affect the
933 // state, as they push and pop the traversal state before and after
934 // traversal of its children.
935 return FALSE;
936 }
937
938 // Doc from superclass.
939 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)940 SoSeparator::getPrimitiveCount(SoGetPrimitiveCountAction * action)
941 {
942 SoSeparator::doAction((SoAction *)action);
943 }
944
945 // Doc from superclass.
946 void
notify(SoNotList * nl)947 SoSeparator::notify(SoNotList * nl)
948 {
949 inherited::notify(nl);
950
951 // lock before using the cache pointers so that we know the pointers
952 // are valid while reading them
953 PRIVATE(this)->lock();
954 if (PRIVATE(this)->bboxcache) PRIVATE(this)->bboxcache->invalidate();
955 PRIVATE(this)->invalidateGLCaches();
956 PRIVATE(this)->hassoundchild = SoSeparatorP::MAYBE;
957 PRIVATE(this)->unlock();
958 }
959
960 /*!
961 This is an internal Open Inventor method. We've implemented
962 view frustum culling in a different manner. Let us know if
963 you need this function, and we'll consider implementing it.
964 */
965 SbBool
cullTest(SoGLRenderAction * COIN_UNUSED_ARG (action),int & COIN_UNUSED_ARG (cullresults))966 SoSeparator::cullTest(SoGLRenderAction * COIN_UNUSED_ARG(action), int & COIN_UNUSED_ARG(cullresults))
967 {
968 COIN_OBSOLETED();
969 return FALSE;
970 }
971
972 // Doc from superclass.
973 SbBool
readInstance(SoInput * in,unsigned short flags)974 SoSeparator::readInstance(SoInput * in, unsigned short flags)
975 {
976 return inherited::readInstance(in, flags);
977 }
978
979 // *************************************************************************
980
981 SbBool
doCull(SoSeparatorP * thisp,SoState * state,SbBool (* cullfunc)(SoState *,const SbBox3f &,const SbBool))982 SoSeparatorP::doCull(SoSeparatorP * thisp, SoState * state,
983 SbBool (* cullfunc)(SoState *, const SbBox3f &, const SbBool))
984 {
985 if (PUBLIC(thisp)->renderCulling.getValue() == SoSeparator::OFF) return FALSE;
986 if (SoCullElement::completelyInside(state)) return FALSE;
987
988 SbBool outside = FALSE;
989 if (thisp->bboxcache &&
990 thisp->bboxcache->isValid(state)) {
991 const SbBox3f & bbox = thisp->bboxcache->getProjectedBox();
992 if (!bbox.isEmpty()) {
993 outside = (*cullfunc)(state, bbox, TRUE);
994 }
995 }
996
997 #if 0
998 // temporarily disabled. setNodeFlag() needs current path, which is
999 // unavailable here
1000 if (outside && SoProfiler::isEnabled()) {
1001 SoProfilerElement * elt = SoProfilerElement::get(state);
1002 if (elt) {
1003 // FIXME: need current path to set this flag. move outside cullTest()?
1004 // elt->getProfilingData().setNodeFlag(PUBLIC(thisp)->getCurPath(), SbProfilingData::CULLED_FLAG, TRUE);
1005 }
1006 }
1007 #endif
1008
1009 return outside;
1010 }
1011
1012 /*!
1013 Internal method which do view frustum culling. For now, view frustum
1014 culling is performed if the renderCulling field is \c AUTO or \c ON,
1015 and the bounding box cache is valid.
1016
1017 Returns \c TRUE if this separator is outside view frustum, \c FALSE
1018 if inside.
1019 */
1020 SbBool
cullTest(SoState * state)1021 SoSeparator::cullTest(SoState * state)
1022 {
1023 return SoSeparatorP::doCull(&PRIVATE(this).get(), state, SoCullElement::cullBox);
1024 }
1025
1026 //
1027 // Performs a cull test on this node. This call will not update
1028 // the SoCullElement, so it can be called without calling
1029 // state->push() first.
1030 //
1031 SbBool
cullTestNoPush(SoState * state)1032 SoSeparator::cullTestNoPush(SoState * state)
1033 {
1034 return SoSeparatorP::doCull(&PRIVATE(this).get(), state, SoCullElement::cullTest);
1035 }
1036
1037 // *************************************************************************
1038
1039 #undef PRIVATE
1040 #undef PUBLIC
1041 #undef GLCACHE_DEBUG
1042