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 SoRenderManager Inventor/SoRenderManager.h
35   \brief The SoRenderManager class is used for controlling the rendering of a scene graph.
36 
37   You can use this class to configure things like clipping planes,
38   rendering mode, stereo rendering and the background color. In
39   earlier versions of Coin/Inventor, this was set up in the GUI
40   toolkits, making it quite hard to make a new Coin viewer for another
41   GUI toolkit. With this new class all that is needed is to create one
42   SoRenderManager instance per viewer/view.
43 
44   This class does not handle events for the scene graph/viewer. To do
45   that you can use the \ref SoEventManager class.
46 
47   \since Coin 3.0
48 */
49 
50 /*! \file SoRenderManager.h */
51 #include <Inventor/SoRenderManager.h>
52 
53 #include <algorithm>
54 //FIXME:Need this include early, since including it via SoRenderManagerP.h will cause problems for cygwin. Don't understand the root cause BFG 20090629
55 #include <vector>
56 
57 #include <Inventor/system/gl.h>
58 #include <Inventor/nodes/SoInfo.h>
59 #include <Inventor/nodes/SoCamera.h>
60 #include <Inventor/elements/SoDrawStyleElement.h>
61 #include <Inventor/elements/SoComplexityTypeElement.h>
62 #include <Inventor/elements/SoPolygonOffsetElement.h>
63 #include <Inventor/elements/SoMaterialBindingElement.h>
64 #include <Inventor/elements/SoOverrideElement.h>
65 #include <Inventor/elements/SoTextureOverrideElement.h>
66 #include <Inventor/elements/SoTextureQualityElement.h>
67 #include <Inventor/elements/SoLightModelElement.h>
68 #include <Inventor/elements/SoLazyElement.h>
69 #include <Inventor/actions/SoGLRenderAction.h>
70 #include <Inventor/actions/SoAudioRenderAction.h>
71 #include <Inventor/actions/SoGLRenderAction.h>
72 #include <Inventor/sensors/SoOneShotSensor.h>
73 #include <Inventor/fields/SoSFTime.h>
74 #include <Inventor/misc/SoAudioDevice.h>
75 #include <Inventor/SoDB.h>
76 
77 #include "coindefs.h"
78 #include "tidbitsp.h"
79 #include "misc/AudioTools.h"
80 #include "coindefs.h"
81 
82 #if BOOST_WORKAROUND(COIN_MSVC, <= COIN_MSVC_6_0_VERSION)
83 // symbol length truncation
84 #pragma warning(disable:4786)
85 #endif // VC6.0
86 
87 #include "SoRenderManagerP.h"
88 
89 /*!
90   \enum SoRenderManager::RenderMode
91 
92   Sets how rendering of primitives is done.
93 */
94 
95 /*!
96   \var SoRenderManager::RenderMode SoRenderManager::AS_IS
97 
98   Render primitives as they are described in the scenegraph.
99 */
100 
101 /*!
102   \var SoRenderManager::RenderMode SoRenderManager::WIREFRAME
103 
104   Render polygons as wireframe
105 */
106 
107 /*!
108   \var SoRenderManager::RenderMode SoRenderManager::POINTS
109 
110   Render only the vertices of the polygons and lines.
111 */
112 
113 /*!
114   \var SoRenderManager::RenderMode SoRenderManager::WIREFRAME_OVERLAY
115 
116   Render a wireframe overlay in addition to the AS_IS mode
117 */
118 
119 /*!
120   \var SoRenderManager::RenderMode SoRenderManager::HIDDEN_LINE
121 
122   As WIREFRAME, but culls lines which would otherwise not be shown due
123   to geometric culling.
124 */
125 
126 /*!
127   \var SoRenderManager::RenderMode SoRenderManager::BOUNDING_BOX
128 
129   Only show the bounding box of each object.
130 */
131 
132 /*!
133   \enum SoRenderManager::StereoMode
134 
135   Manages how to render steroscopic images.
136 */
137 
138 /*!
139   \var SoRenderManager::StereoMode SoRenderManager::MONO
140 
141   No stereoscopic rendering
142 */
143 
144 /*!
145   \var SoRenderManager::StereoMode SoRenderManager::ANAGLYPH
146 
147   Anaglyph rendering is used to provide a steroscopic 3D effect, when
148   viewed with 3D glasses. The image is made up of two color layers
149   which are superimposed on each other, and appears as 3 dimensional
150   when viewed through corresponding colored filters(glases)
151 */
152 
153 /*!
154   \var SoRenderManager::StereoMode SoRenderManager::SEPARATE_OUTPUT
155 
156   Send output to separate buffers.
157 */
158 
159 /*!
160   \var SoRenderManager::StereoMode SoRenderManager::QUAD_BUFFER
161 
162   Same as SEPARATE_OUTPUT, SEPARATE_OUTPUT is more commonly known as
163   QUAD_BUFFER, when also using double buffering.
164 */
165 
166 /*!
167   \var SoRenderManager::StereoMode SoRenderManager::INTERLEAVED_ROWS
168 
169   Render every second row as left and right image. If rendered with a
170   polarized projector, polarized filters kan be used to give a 3D
171   effect.
172 */
173 
174 /*!
175   \var SoRenderManager::StereoMode SoRenderManager::INTERLEAVED_COLUMNS
176 
177   Render every second column as left and right image. If rendered with
178   a polarized projector, polarized filters kan be used to give a 3D
179   effect.
180 */
181 
182 /*!
183   \enum SoRenderManager::BufferType
184 
185   Buffering strategy
186 */
187 
188 /*!
189   \var SoRenderManager::BufferType SoRenderManager::BUFFER_SINGLE
190 
191   Output to one buffer
192 */
193 
194 /*!
195   \var SoRenderManager::BufferType SoRenderManager::BUFFER_DOUBLE
196 
197   Alternate between output buffers
198 */
199 
200 /*!
201   \enum SoRenderManager::AutoClippingStrategy
202 
203   Strategy for adjusting camera near/far clipping plane
204 */
205 
206 /*!
207   \var SoRenderManager::AutoClippingStrategy SoRenderManager::NO_AUTO_CLIPPING
208 
209   Turn off automatic clipping. The user needs to set the correct values in the camera.
210 */
211 
212 /*!
213   \var SoRenderManager::AutoClippingStrategy SoRenderManager::FIXED_NEAR_PLANE
214 
215   Keep near plane at a fixed distance from the camera. The far plane is always set
216   at the end of the bounding box.
217 */
218 
219 /*!
220   \var SoRenderManager::AutoClippingStrategy SoRenderManager::VARIABLE_NEAR_PLANE
221 
222   Variable adjustment of the nearplane relative to the camera.
223 */
224 
225 #define PRIVATE(p) (p->pimpl)
226 #define PUBLIC(p) (p->publ)
227 
228 /*!
229   Constructor.
230 */
SoRenderManager(void)231 SoRenderManager::SoRenderManager(void)
232 {
233   assert(SoDB::isInitialized() && "SoDB::init() has not been invoked");
234 
235   PRIVATE(this) = new SoRenderManagerP(this);
236 
237   PRIVATE(this)->dummynode = new SoInfo;
238   PRIVATE(this)->dummynode->ref();
239 
240   PRIVATE(this)->rootsensor = NULL;
241   PRIVATE(this)->scene = NULL;
242   PRIVATE(this)->camera = NULL;
243   PRIVATE(this)->rendercb = NULL;
244   PRIVATE(this)->rendercbdata = NULL;
245 
246   PRIVATE(this)->stereostencilmask = NULL;
247   PRIVATE(this)->superimpositions = NULL;
248 
249   PRIVATE(this)->doublebuffer = TRUE;
250   PRIVATE(this)->deleteaudiorenderaction = TRUE;
251   PRIVATE(this)->deleteglaction = TRUE;
252   PRIVATE(this)->isactive = TRUE;
253   PRIVATE(this)->texturesenabled = TRUE;
254 
255   PRIVATE(this)->nearplanevalue = 0.6f;
256   PRIVATE(this)->stereooffset = 1.0f;
257   PRIVATE(this)->isrgbmode = TRUE;
258   PRIVATE(this)->backgroundcolor.setValue(0.0f, 0.0f, 0.0f, 0.0f);
259   PRIVATE(this)->backgroundindex = 0;
260   PRIVATE(this)->overlaycolor = SbColor(1.0f, 0.0f, 0.0f).getPackedValue();
261   PRIVATE(this)->stereostencilmaskvp = SbViewportRegion(0, 0);
262 
263   PRIVATE(this)->stereostenciltype = SoRenderManager::MONO;
264   PRIVATE(this)->rendermode = SoRenderManager::AS_IS;
265   PRIVATE(this)->stereomode = SoRenderManager::MONO;
266   PRIVATE(this)->autoclipping = SoRenderManager::NO_AUTO_CLIPPING;
267   PRIVATE(this)->redrawpri = SoRenderManager::getDefaultRedrawPriority();
268   PRIVATE(this)->redrawshot =
269     new SoOneShotSensor(SoRenderManagerP::redrawshotTriggeredCB, this);
270   PRIVATE(this)->redrawshot->setPriority(PRIVATE(this)->redrawpri);
271 
272   PRIVATE(this)->glaction = new SoGLRenderAction(SbViewportRegion(400, 400));
273   PRIVATE(this)->audiorenderaction = new SoAudioRenderAction;
274 
275   PRIVATE(this)->clipsensor =
276     new SoNodeSensor(SoRenderManagerP::updateClippingPlanesCB, PRIVATE(this));
277   PRIVATE(this)->clipsensor->setPriority(this->getRedrawPriority() - 1);
278 
279 }
280 
281 /*!
282   Destructor.
283  */
~SoRenderManager()284 SoRenderManager::~SoRenderManager()
285 {
286   PRIVATE(this)->dummynode->unref();
287 
288   if (PRIVATE(this)->deleteglaction) delete PRIVATE(this)->glaction;
289   if (PRIVATE(this)->deleteaudiorenderaction) delete PRIVATE(this)->audiorenderaction;
290   delete PRIVATE(this)->rootsensor;
291   delete PRIVATE(this)->redrawshot;
292 
293   if (PRIVATE(this)->superimpositions != NULL) {
294     while (PRIVATE(this)->superimpositions->getLength() > 0) {
295       this->removeSuperimposition((Superimposition *)(*PRIVATE(this)->superimpositions)[0]);
296     }
297     delete PRIVATE(this)->superimpositions;
298   }
299 
300   delete PRIVATE(this)->clipsensor;
301 
302   if (PRIVATE(this)->scene)
303     PRIVATE(this)->scene->unref();
304   this->setCamera(NULL);
305 
306   delete PRIVATE(this);
307 }
308 
309 /*!
310   Set the node which is top of the scene graph we're managing.  The \a
311   sceneroot node reference count will be increased by 1, and any
312   previously set scene graph top node will have it's reference count
313   decreased by 1.
314 
315   \sa getSceneGraph()
316 */
317 void
setSceneGraph(SoNode * const sceneroot)318 SoRenderManager::setSceneGraph(SoNode * const sceneroot)
319 {
320   this->detachClipSensor();
321   this->detachRootSensor();
322   // Don't unref() until after we've set up the new root, in case the
323   // old root == the new sceneroot. (Just to be that bit more robust.)
324   SoNode * oldroot = PRIVATE(this)->scene;
325 
326   PRIVATE(this)->scene = sceneroot;
327 
328   if (PRIVATE(this)->scene) {
329     PRIVATE(this)->scene->ref();
330     this->attachRootSensor(PRIVATE(this)->scene);
331     this->attachClipSensor(PRIVATE(this)->scene);
332   }
333 
334   if (oldroot) oldroot->unref();
335 }
336 
337 /*!
338   Returns the pointer to root of scene graph.
339  */
340 SoNode *
getSceneGraph(void) const341 SoRenderManager::getSceneGraph(void) const
342 {
343   return PRIVATE(this)->scene;
344 }
345 
346 /*!
347   Sets the camera to be used.
348 */
349 void
setCamera(SoCamera * camera)350 SoRenderManager::setCamera(SoCamera * camera)
351 {
352   // avoid unref() then ref() on the same node
353   if (camera == PRIVATE(this)->camera) return;
354 
355   if (PRIVATE(this)->camera) {
356     PRIVATE(this)->camera->unref();
357   }
358   PRIVATE(this)->camera = camera;
359   if (camera) camera->ref();
360 }
361 
362 /*!
363   Returns the current camera.
364 */
365 SoCamera *
getCamera(void) const366 SoRenderManager::getCamera(void) const
367 {
368   return PRIVATE(this)->camera;
369 }
370 
371 /*
372   Internal callback
373 
374   \param[in] data Pointer to SoRenderManager
375 
376   \deprecated Will be made private in a later version of Coin
377 */
378 void
nodesensorCB(void * data,SoSensor *)379 SoRenderManager::nodesensorCB(void * data, SoSensor * /* sensor */)
380 {
381 #if COIN_DEBUG && 0 // debug
382   SoDebugError::postInfo("SoRenderManager::nodesensorCB",
383                          "detected change in scene graph");
384 #endif // debug
385   ((SoRenderManager *)data)->scheduleRedraw();
386 }
387 
388 /*!
389   Attaches this SoRenderManagers rootsensor to a scene
390 
391   \param[in] sceneroot scene to attach to
392 
393   \deprecated Will be private available in Coin 4
394 */
395 void
attachRootSensor(SoNode * const sceneroot)396 SoRenderManager::attachRootSensor(SoNode * const sceneroot)
397 {
398   if (!PRIVATE(this)->rootsensor) {
399     (SoRenderManagerRootSensor::debug()) ?
400       PRIVATE(this)->rootsensor = new SoRenderManagerRootSensor(SoRenderManager::nodesensorCB, this):
401       PRIVATE(this)->rootsensor = new SoNodeSensor(SoRenderManager::nodesensorCB, this);
402     // set a high priority on the root sensor. The actual redraw
403     // scheduling is handled by the redraw sensor at the correct
404     // priority (root sensor callback triggers the redraw sensor). Set
405     // priority to 1 in the normal case, and 0 if the redraw priority
406     // is actually set to 0
407     PRIVATE(this)->rootsensor->setPriority(PRIVATE(this)->redrawpri == 0 ? 0 : 1);
408   }
409   PRIVATE(this)->rootsensor->attach(sceneroot);
410 }
411 
412 /*
413   Detaches the rootsensor from all tracked scenes
414 
415   \deprecated Will not be available in Coin 4
416 */
417 void
detachRootSensor(void)418 SoRenderManager::detachRootSensor(void)
419 {
420   if (PRIVATE(this)->rootsensor) {
421     PRIVATE(this)->rootsensor->detach();
422   }
423 }
424 
425 /*
426   Attaches this SoRenderManagers clipsensor to a scene
427 
428   \param[in] sceneroot scene to attach to
429 
430   \deprecated Will not be available in Coin 4
431 */
432 void
attachClipSensor(SoNode * const sceneroot)433 SoRenderManager::attachClipSensor(SoNode * const sceneroot)
434 {
435   PRIVATE(this)->clipsensor->attach(sceneroot);
436   if (PRIVATE(this)->autoclipping != SoRenderManager::NO_AUTO_CLIPPING) {
437     PRIVATE(this)->clipsensor->schedule();
438   }
439 }
440 
441 /*
442   Detaches the clipsensor from all tracked scenes
443 
444   \deprecated Will not be available in Coin 4
445 */
446 void
detachClipSensor(void)447 SoRenderManager::detachClipSensor(void)
448 {
449   if (PRIVATE(this)->clipsensor->isScheduled()) {
450     PRIVATE(this)->clipsensor->unschedule();
451   }
452   if (PRIVATE(this)->clipsensor->getAttachedNode()) {
453     PRIVATE(this)->clipsensor->detach();
454   }
455 }
456 
457 /*!
458   Clears buffers with the backgroundcolor set correctly
459 
460   \param[in] color Set to \c TRUE if color buffer should be cleared
461   \param[in] depth Set to \c TRUE if depth buffer should be cleared
462 */
463 void
clearBuffers(SbBool color,SbBool depth)464 SoRenderManager::clearBuffers(SbBool color, SbBool depth)
465 {
466   GLbitfield mask = 0;
467   if (color) mask |= GL_COLOR_BUFFER_BIT;
468   if (depth) mask |= GL_DEPTH_BUFFER_BIT;
469   const SbColor4f bgcol = PRIVATE(this)->backgroundcolor;
470   glClearColor(bgcol[0], bgcol[1], bgcol[2], bgcol[3]);
471   glClear(mask);
472 }
473 
474 /*
475   Internal callback
476 
477   \param[in] userdata GLbitfield mask
478   \param[in] action Calling action
479 
480   \deprecated Will be made private in a later version of Coin
481 */
482 void
prerendercb(void * userdata,SoGLRenderAction * action)483 SoRenderManager::prerendercb(void * userdata, SoGLRenderAction * action)
484 {
485   // remove callback again
486   action->removePreRenderCallback(prerendercb, userdata);
487   // MSVC7 on 64-bit Windows wants it to go through this cast.
488   const uintptr_t bitfield = (uintptr_t)userdata;
489   GLbitfield mask = (GLbitfield)bitfield;
490 
491 #if COIN_DEBUG && 0 // debug
492   GLint view[4];
493   glGetIntegerv(GL_VIEWPORT, view);
494   SoDebugError::postInfo("SoRenderManager::prerendercb",
495                          "GL_VIEWPORT=<%d, %d, %d, %d>",
496                          view[0], view[1], view[2], view[3]);
497 #endif // debug
498 
499   // clear the viewport
500   glClear(mask);
501 }
502 
503 /*!
504   Add a superimposition for this scene graph. A superimposition can
505   either be a scene rendered in the background, or a scene rendered in
506   the front of the actual scene graph.
507 
508   This is useful, for instance, if you want to add a gradient or an
509   image in the background, and for having some HUD info in the
510   foreground.
511 
512   Please note that if you use superimpositions, multipass antialiasing
513   have to be handled in the render manager, and not in
514   SoGLRenderAction, so the pass update features in SoGLRenderAction
515   will be disabled.
516 
517   \sa SoGLRenderAction::setNumPasses()
518   \sa SoGLRenderAction::setPassUpdate()
519   \sa SoGLRenderAction::setPassCallback()
520 */
521 SoRenderManager::Superimposition *
addSuperimposition(SoNode * scene,uint32_t flags)522 SoRenderManager::addSuperimposition(SoNode * scene,
523                                     uint32_t flags)
524 {
525   if (!PRIVATE(this)->superimpositions) {
526     PRIVATE(this)->superimpositions = new SbPList;
527   }
528   Superimposition * s = new Superimposition(scene, TRUE, this, flags);
529   PRIVATE(this)->superimpositions->append(s);
530   return s;
531 }
532 
533 /*!
534   Removes a superimposition.
535 
536   \sa addSuperimposition()
537 */
538 void
removeSuperimposition(Superimposition * s)539 SoRenderManager::removeSuperimposition(Superimposition * s)
540 {
541   int idx = -1;
542   if (!PRIVATE(this)->superimpositions) goto error;
543   if ((idx = PRIVATE(this)->superimpositions->find(s)) == -1) goto error;
544 
545   PRIVATE(this)->superimpositions->remove(idx);
546   delete s;
547   return;
548 
549  error:
550 #if COIN_DEBUG
551   SoDebugError::post("SoRenderManager::removeSuperimposition",
552                      "no such superimposition");
553 #endif // COIN_DEBUG
554   return;
555 }
556 
557 
558 /*!
559   Render the scene graph.
560 
561   All SbBool arguments should normally be \c TRUE, but they can be set
562   to \c FALSE to optimize for special cases (e.g. when doing wireframe
563   rendering one doesn't need a depth buffer).
564 
565   \param[in] clearwindow If set to \c TRUE, clear the rendering buffer
566   before drawing.
567 
568   \param[in] clearzbuffer If set to \c TRUE, clear the depth buffer
569   values before rendering.
570 
571 */
572 void
render(const SbBool clearwindow,const SbBool clearzbuffer)573 SoRenderManager::render(const SbBool clearwindow, const SbBool clearzbuffer)
574 {
575   // FIXME: according to a user, TGS Inventor seems to disable the
576   // redraw SoOneShotSensor while the scene graph is being rendered,
577   // which Coin does not do. SGI Inventor probably has the same
578   // behavior as TGS Inventor. (Should investigate this.)
579   //
580   // pederb suggests keeping the current behavior in Coin, even though
581   // this may cause trouble for code being ported from SGI / TGS
582   // Inventor, as it is convenient to "touch()" a node for triggering
583   // continuous animation. Besides, making Coin compatible with SGI
584   // (?) / TGS Inventor now may cause problems for existing Coin
585   // client code.
586   //
587   // I'm however not too happy with this fairly large incompatibility.
588   // Any usable suggestions for a resolution of this problem would be
589   // welcome.
590   //
591   // 20050809 mortene.
592 
593   if (PRIVATE(this)->scene &&
594       // Order is important below, because we don't want to call
595       // SoAudioDevice::instance() unless we need to -- as it triggers
596       // loading the OpenAL library, which should only be loaded on
597       // demand.
598       coin_sound_should_traverse() &&
599       SoAudioDevice::instance()->haveSound() &&
600       SoAudioDevice::instance()->isEnabled())
601     PRIVATE(this)->audiorenderaction->apply(PRIVATE(this)->scene);
602 
603   SoGLRenderAction * action = PRIVATE(this)->glaction;
604   const int numpasses = action->getNumPasses();
605 
606   // extra care has to be taken if the user attempts to do multipass
607   // antialiasing while using superimpositions
608   if (numpasses > 1 &&
609       PRIVATE(this)->superimpositions &&
610       PRIVATE(this)->superimpositions->getLength()) {
611     action->setNumPasses(1);
612 
613     // FIXME: the pass update callback from SoGLRenderAction is not
614     // supported, but this is not a feature anybody is using anymore,
615     // I suspect. We'll document this in the addSuperimposition()
616     // documentation
617 
618     // render the first pass normally
619     action->setCurPass(0, numpasses);
620     this->render(action, TRUE, clearwindow, clearzbuffer);
621 
622     // check if we have an accumulation buffer, and render additional passes
623     GLint accumbits;
624     glGetIntegerv(GL_ACCUM_RED_BITS, &accumbits);
625     if (!action->hasTerminated() && accumbits > 0) {
626       const float fraction = 1.0f / float(numpasses);
627       glAccum(GL_LOAD, fraction);
628 
629       for (int i = 1; (i < numpasses) && !action->hasTerminated(); i++) {
630         action->setCurPass(i, numpasses);
631         this->render(action, TRUE, TRUE, TRUE);
632         glAccum(GL_ACCUM, fraction);
633       }
634       glAccum(GL_RETURN, 1.0f);
635     }
636     action->setCurPass(0, 1);
637     action->setNumPasses(numpasses);
638   }
639   else {
640     // let SoGLRenderAction handle the accumulation buffer
641     this->render(PRIVATE(this)->glaction, TRUE, clearwindow, clearzbuffer);
642   }
643 }
644 
645 /*!
646   \copydetails SoRenderManager::render(const SbBool clearwindow, const SbBool clearzbuffer)
647 
648   \param[in] initmatrices if true, the projection and modelview
649   matrices are reset to identity
650   \param[in] action Renders with a user supplied action
651 */
652 void
render(SoGLRenderAction * action,const SbBool initmatrices,const SbBool clearwindow,const SbBool clearzbuffer)653 SoRenderManager::render(SoGLRenderAction * action,
654                         const SbBool initmatrices,
655                         const SbBool clearwindow,
656                         const SbBool clearzbuffer)
657 {
658   SbBool clearwindow_tmp = clearwindow; // make sure we only clear the color buffer once
659   PRIVATE(this)->invokePreRenderCallbacks();
660 
661   if (PRIVATE(this)->superimpositions) {
662     for (int i = 0; i < PRIVATE(this)->superimpositions->getLength(); i++) {
663       Superimposition * s = (Superimposition *) (*PRIVATE(this)->superimpositions)[i];
664       if (s->getStateFlags() & Superimposition::BACKGROUND) {
665         s->render(action, clearwindow_tmp);
666         clearwindow_tmp = FALSE;
667       }
668     }
669   }
670 
671   (this->getStereoMode() == SoRenderManager::MONO) ?
672     this->renderSingle(action, initmatrices, clearwindow_tmp, clearzbuffer):
673     this->renderStereo(action, initmatrices, clearwindow_tmp, clearzbuffer);
674 
675   if (PRIVATE(this)->superimpositions) {
676     for (int i = 0; i < PRIVATE(this)->superimpositions->getLength(); i++) {
677       Superimposition * s = (Superimposition *) (*PRIVATE(this)->superimpositions)[i];
678       if (!(s->getStateFlags() & Superimposition::BACKGROUND)) {
679         s->render(action);
680       }
681     }
682   }
683 
684   PRIVATE(this)->invokePostRenderCallbacks();
685 }
686 
687 /*!
688   Convenience function for \ref SoRenderManager::renderScene
689 
690   \param[in] action Renders with a user supplied action
691 
692   \param[in] initmatrices if true, the projection and modelview
693   matrices are reset to identity
694 
695   \param[in] clearwindow If set to \c TRUE, clear the rendering buffer
696   before drawing.
697 
698   \param[in] clearzbuffer If set to \c TRUE, clear the depth buffer
699   values before rendering.
700 */
701 void
actuallyRender(SoGLRenderAction * action,const SbBool initmatrices,const SbBool clearwindow,const SbBool clearzbuffer)702 SoRenderManager::actuallyRender(SoGLRenderAction * action,
703                                 const SbBool initmatrices,
704                                 const SbBool clearwindow,
705                                 const SbBool clearzbuffer)
706 {
707   GLbitfield mask = 0;
708   if (clearwindow) mask |= GL_COLOR_BUFFER_BIT;
709   if (clearzbuffer) mask |= GL_DEPTH_BUFFER_BIT;
710 
711   if (initmatrices) {
712     glMatrixMode(GL_PROJECTION);
713     glLoadIdentity();
714     glMatrixMode(GL_MODELVIEW);
715     glLoadIdentity();
716   }
717 
718   // If there has been changes in the scene graph leading to a node
719   // sensor detect and schedule before we've gotten around to serving
720   // the current redraw -- remove it. This will prevent infinite loops
721   // in the case of scenegraph modifications between a nodesensor
722   // trigger and SoRenderManager::render() actually being called. It
723   // will also help us avoid "double redraws" at expose events.
724   PRIVATE(this)->lock();
725   if (PRIVATE(this)->rootsensor && PRIVATE(this)->rootsensor->isScheduled()) {
726 #if COIN_DEBUG && 0 // debug
727     SoDebugError::postInfo("SoRenderManager::render",
728                            "rootsensor unschedule");
729 #endif // debug
730     PRIVATE(this)->rootsensor->unschedule();
731   }
732   PRIVATE(this)->unlock();
733   // Apply the SoGLRenderAction to the scenegraph root.
734   if (PRIVATE(this)->scene) {
735     this->renderScene(action, PRIVATE(this)->scene, (uint32_t) mask);
736   }
737 
738   // Automatically re-triggers rendering if any animation stuff is
739   // connected to the realTime field.
740   if (SoRenderManager::isRealTimeUpdateEnabled()) {
741     // FIXME: it would be more elegant to use a private field class
742     // inheriting SoSFTime ("SFRealTime") which could just be
743     // touch()'ed, and which would do lazy reading of time-of-day on
744     // demand. 20000316 mortene.
745     SoField * realtime = SoDB::getGlobalField("realTime");
746     if (realtime && (realtime->getTypeId() == SoSFTime::getClassTypeId())) {
747       // Note that this should not get in the way of a
748       // app-programmer controlled realTime field, as
749       // enableRealTimeUpdate(FALSE) should then have been called.
750       ((SoSFTime *)realtime)->setValue(SbTime::getTimeOfDay());
751     }
752   }
753 }
754 
755 /*!
756   Renders a scene and applies clear state as given by this renderManager
757 
758   \param[in] action Action to apply
759   \param[in] scene Scene to render
760   \param[in] clearmask mask to pass to glClear
761 */
762 void
renderScene(SoGLRenderAction * action,SoNode * scene,uint32_t clearmask)763 SoRenderManager::renderScene( SoGLRenderAction * action,
764                               SoNode * scene,
765                               uint32_t clearmask)
766 {
767   if (clearmask) {
768     if (clearmask & GL_COLOR_BUFFER_BIT) {
769       if (PRIVATE(this)->isrgbmode) {
770         const SbColor4f bgcol = PRIVATE(this)->backgroundcolor;
771         glClearColor(bgcol[0], bgcol[1], bgcol[2], bgcol[3]);
772       }
773       else {
774         glClearIndex((GLfloat) PRIVATE(this)->backgroundindex);
775       }
776     }
777     // Registering a callback is needed since the correct GL viewport
778     // is set by SoGLRenderAction before rendering. It might not be
779     // correct when we get here.
780     // This callback is removed again in the prerendercb function
781     action->addPreRenderCallback(this->prerendercb, (void*) (uintptr_t) clearmask);
782   }
783   action->apply(scene);
784 }
785 
786 /*!
787   \brief Render once in correct draw style
788 
789   \copydoc SoRenderManager::actuallyRender
790 */
791 void
renderSingle(SoGLRenderAction * action,SbBool initmatrices,SbBool clearwindow,SbBool clearzbuffer)792 SoRenderManager::renderSingle(SoGLRenderAction * action,
793                               SbBool initmatrices,
794                               SbBool clearwindow,
795                               SbBool clearzbuffer)
796 {
797   SoState * state = action->getState();
798   state->push();
799 
800   SoNode * node = PRIVATE(this)->dummynode;
801 
802   if (!this->isTexturesEnabled()) {
803     SoTextureQualityElement::set(state, node, 0.0f);
804     SoTextureOverrideElement::setQualityOverride(state, TRUE);
805   }
806   switch (this->getRenderMode()) {
807   case SoRenderManager::AS_IS:
808     this->actuallyRender(action, initmatrices, clearwindow, clearzbuffer);
809     break;
810   case SoRenderManager::WIREFRAME:
811     SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
812     SoLightModelElement::set(state, node, SoLightModelElement::BASE_COLOR);
813     SoOverrideElement::setDrawStyleOverride(state, node, TRUE);
814     SoOverrideElement::setLightModelOverride(state, node, TRUE);
815     this->actuallyRender(action, initmatrices, clearwindow, clearzbuffer);
816     break;
817   case SoRenderManager::POINTS:
818     SoDrawStyleElement::set(state, node, SoDrawStyleElement::POINTS);
819     SoLightModelElement::set(state, node, SoLightModelElement::BASE_COLOR);
820     SoOverrideElement::setDrawStyleOverride(state, node, TRUE);
821     SoOverrideElement::setLightModelOverride(state, node, TRUE);
822     this->actuallyRender(action, initmatrices, clearwindow, clearzbuffer);
823     break;
824   case SoRenderManager::HIDDEN_LINE:
825     {
826       // must clear before setting draw mask
827       this->clearBuffers(TRUE, TRUE);
828 
829       // only draw into depth buffer
830       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
831       SoMaterialBindingElement::set(state, node, SoMaterialBindingElement::OVERALL);
832       SoLightModelElement::set(state, node, SoLightModelElement::BASE_COLOR);
833       SoPolygonOffsetElement::set(state, node, 1.0f, 1.0f,
834                                   SoPolygonOffsetElement::FILLED, TRUE);
835       SoOverrideElement::setPolygonOffsetOverride(state, node, TRUE);
836       SoOverrideElement::setLightModelOverride(state, node, TRUE);
837       SoOverrideElement::setMaterialBindingOverride(state, node, TRUE);
838       this->actuallyRender(action, initmatrices, FALSE, FALSE);
839 
840       // reenable draw masks
841       glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
842       SoPolygonOffsetElement::set(state, node, 0.0f, 0.0f,
843                                   SoPolygonOffsetElement::FILLED, FALSE);
844       SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
845       SoOverrideElement::setDrawStyleOverride(state, node, TRUE);
846       SoOverrideElement::setMaterialBindingOverride(state, node, FALSE);
847       this->actuallyRender(action, initmatrices, FALSE, FALSE);
848     }
849     break;
850   case SoRenderManager::WIREFRAME_OVERLAY:
851       SoPolygonOffsetElement::set(state, node, 1.0f, 1.0f,
852                                   SoPolygonOffsetElement::FILLED, TRUE);
853       SoOverrideElement::setPolygonOffsetOverride(state, node, TRUE);
854       this->actuallyRender(action, initmatrices, clearwindow, clearzbuffer);
855       SoPolygonOffsetElement::set(state, node, 0.0f, 0.0f,
856                                   SoPolygonOffsetElement::FILLED, FALSE);
857 
858       SoLazyElement::setPacked(state, node, 1, &PRIVATE(this)->overlaycolor, TRUE);
859       SoLightModelElement::set(state, node, SoLightModelElement::BASE_COLOR);
860       SoMaterialBindingElement::set(state, node, SoMaterialBindingElement::OVERALL);
861       SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
862       SoOverrideElement::setLightModelOverride(state, node, TRUE);
863       SoOverrideElement::setDiffuseColorOverride(state, node, TRUE);
864       SoOverrideElement::setMaterialBindingOverride(state, node, TRUE);
865       SoOverrideElement::setDrawStyleOverride(state, node, TRUE);
866       this->actuallyRender(action, initmatrices, FALSE, FALSE);
867     break;
868 
869   case SoRenderManager::BOUNDING_BOX:
870     SoDrawStyleElement::set(state, node, SoDrawStyleElement::LINES);
871     SoLightModelElement::set(state, node, SoLightModelElement::BASE_COLOR);
872     SoComplexityTypeElement::set(state, node, SoComplexityTypeElement::BOUNDING_BOX);
873     SoOverrideElement::setDrawStyleOverride(state, node, TRUE);
874     SoOverrideElement::setLightModelOverride(state, node, TRUE);
875     SoOverrideElement::setComplexityTypeOverride(state, node, TRUE);
876     this->actuallyRender(action, initmatrices, clearwindow, clearzbuffer);
877     break;
878   default:
879     assert(0 && "unknown rendering mode");
880     break;
881   }
882   state->pop();
883 }
884 
885 /*!
886   \brief Render scene according to current stereo mode
887 
888   \copydoc SoRenderManager::actuallyRender
889 */
890 void
renderStereo(SoGLRenderAction * action,SbBool initmatrices,SbBool clearwindow,SbBool clearzbuffer)891 SoRenderManager::renderStereo(SoGLRenderAction * action,
892                               SbBool initmatrices,
893                               SbBool clearwindow,
894                               SbBool clearzbuffer)
895 {
896   if (!PRIVATE(this)->camera) return;
897 
898   this->clearBuffers(TRUE, TRUE);
899   PRIVATE(this)->camera->setStereoAdjustment(PRIVATE(this)->stereooffset);
900 
901   SbBool stenciltestenabled = glIsEnabled(GL_STENCIL_TEST);
902 
903   // left eye
904   PRIVATE(this)->camera->setStereoMode(SoCamera::LEFT_VIEW);
905 
906   switch (PRIVATE(this)->stereomode) {
907   case SoRenderManager::ANAGLYPH:
908     glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
909     this->renderSingle(action, initmatrices, FALSE, FALSE);
910     break;
911   case SoRenderManager::QUAD_BUFFER:
912     glDrawBuffer(PRIVATE(this)->doublebuffer ? GL_BACK_LEFT : GL_FRONT_LEFT);
913     this->renderSingle(action, initmatrices, clearwindow, clearzbuffer);
914     break;
915   case SoRenderManager::INTERLEAVED_ROWS:
916   case SoRenderManager::INTERLEAVED_COLUMNS:
917     this->initStencilBufferForInterleavedStereo();
918     glEnable(GL_STENCIL_TEST);
919     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
920     glStencilFunc(GL_EQUAL, 0x1, 0x1);
921     this->renderSingle(action, initmatrices, clearwindow, clearzbuffer);
922     break;
923   default:
924     assert(0 && "unknown stereo mode");
925     break;
926   }
927 
928   // right eye
929   PRIVATE(this)->camera->setStereoMode(SoCamera::RIGHT_VIEW);
930 
931   switch (PRIVATE(this)->stereomode) {
932   case SoRenderManager::ANAGLYPH:
933     glClear(GL_DEPTH_BUFFER_BIT);
934     glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
935     this->renderSingle(action, initmatrices, FALSE, TRUE);
936     break;
937   case SoRenderManager::QUAD_BUFFER:
938     glDrawBuffer(PRIVATE(this)->doublebuffer ? GL_BACK_RIGHT : GL_FRONT_RIGHT);
939     this->renderSingle(action, initmatrices, clearwindow, clearzbuffer);
940     break;
941   case SoRenderManager::INTERLEAVED_ROWS:
942   case SoRenderManager::INTERLEAVED_COLUMNS:
943     glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
944     this->renderSingle(action, initmatrices, FALSE, FALSE);
945     break;
946   default:
947     assert(0 && "unknown stereo mode");
948     break;
949   }
950 
951   // restore / post render operations
952   PRIVATE(this)->camera->setStereoMode(SoCamera::MONOSCOPIC);
953 
954   switch (PRIVATE(this)->stereomode) {
955   case SoRenderManager::ANAGLYPH:
956     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
957     break;
958   case SoRenderManager::QUAD_BUFFER:
959     glDrawBuffer(PRIVATE(this)->doublebuffer ? GL_BACK : GL_FRONT);
960     break;
961   case SoRenderManager::INTERLEAVED_ROWS:
962   case SoRenderManager::INTERLEAVED_COLUMNS:
963     stenciltestenabled ?
964       glEnable(GL_STENCIL_TEST) :
965       glDisable(GL_STENCIL_TEST);
966     break;
967   default:
968     assert(0 && "unknown stereo mode");
969     break;
970   }
971 }
972 
973 /*!
974   Sets strategy for adjusting camera clipping plane
975 
976   \see SoRenderManager::AutoClippingStrategy
977 */
978 void
setAutoClipping(AutoClippingStrategy autoclipping)979 SoRenderManager::setAutoClipping(AutoClippingStrategy autoclipping)
980 {
981   PRIVATE(this)->autoclipping = autoclipping;
982 
983   if (PRIVATE(this)->scene) {
984     switch (autoclipping) {
985     case SoRenderManager::NO_AUTO_CLIPPING:
986       this->detachClipSensor();
987       break;
988     case SoRenderManager::FIXED_NEAR_PLANE:
989     case SoRenderManager::VARIABLE_NEAR_PLANE:
990       if (!PRIVATE(this)->clipsensor->getAttachedNode()) {
991         PRIVATE(this)->clipsensor->attach(PRIVATE(this)->scene);
992       }
993       PRIVATE(this)->clipsensor->schedule();
994       break;
995     }
996   }
997 }
998 
999 /*!
1000   Initializes stencilbuffers for interleaved stereo
1001 */
1002 void
initStencilBufferForInterleavedStereo(void)1003 SoRenderManager::initStencilBufferForInterleavedStereo(void)
1004 {
1005   const SbViewportRegion & currentvp = PRIVATE(this)->glaction->getViewportRegion();
1006   if (PRIVATE(this)->stereostencilmaskvp == currentvp) { return; } // the common case
1007 
1008   SoRenderManager::StereoMode s = PRIVATE(this)->stereomode;
1009   assert((s == SoRenderManager::INTERLEAVED_ROWS) ||
1010          (s == SoRenderManager::INTERLEAVED_COLUMNS));
1011 
1012   // Find out whether or not we need to regenerate the mask data.
1013   SbBool allocnewmask = (PRIVATE(this)->stereostencilmask == NULL);
1014 
1015   const SbVec2s neworigin = currentvp.getViewportOriginPixels();
1016   const SbVec2s newsize = currentvp.getViewportSizePixels();
1017 
1018   const SbVec2s oldorigin = PRIVATE(this)->stereostencilmaskvp.getViewportOriginPixels();
1019   const SbVec2s oldsize = PRIVATE(this)->stereostencilmaskvp.getViewportSizePixels();
1020 
1021   allocnewmask = allocnewmask ||
1022     ((oldsize[0] + 7) / 8 * oldsize[1]) < ((newsize[0] + 7) / 8 * newsize[1]);
1023 
1024   const SbBool fillmask = allocnewmask || (PRIVATE(this)->stereostenciltype != s) ||
1025     ((s == SoRenderManager::INTERLEAVED_ROWS) && (oldsize[0] != newsize[0]));
1026 
1027   const SbBool layoutchange = !(PRIVATE(this)->stereostencilmaskvp == currentvp);
1028 
1029   const short bytewidth = (newsize[0] + 7) / 8;
1030 
1031   if (allocnewmask) {
1032     delete[] PRIVATE(this)->stereostencilmask;
1033     PRIVATE(this)->stereostencilmask = new GLubyte[bytewidth * newsize[1]];
1034   }
1035 
1036   PRIVATE(this)->stereostencilmaskvp = currentvp;
1037 
1038   if (fillmask) {
1039     GLubyte * mask = PRIVATE(this)->stereostencilmask;
1040 
1041     if (s == SoRenderManager::INTERLEAVED_COLUMNS) {
1042       // alternating columns of 0's and 1's
1043       (void)memset(mask, 0x55, bytewidth * newsize[1]);
1044     }
1045     else {
1046       // alternating rows of 0's and 1's
1047       for (short h=0; h < newsize[1]; h++) {
1048         const GLubyte fill = (h % 2) ? 0xff : 0x00;
1049         (void)memset(mask + (h * bytewidth), fill, bytewidth);
1050       }
1051     }
1052 
1053     PRIVATE(this)->stereostenciltype = s;
1054   }
1055 
1056   if (layoutchange) {
1057     glClearStencil(0x0);
1058 
1059     glClear(GL_STENCIL_BUFFER_BIT);
1060     glStencilFunc(GL_ALWAYS, GL_REPLACE, GL_REPLACE);
1061 
1062     glMatrixMode(GL_MODELVIEW);
1063     glPushMatrix();
1064     glLoadIdentity();
1065     glMatrixMode(GL_PROJECTION);
1066     glPushMatrix();
1067     glLoadIdentity();
1068 
1069     glViewport(neworigin[0], neworigin[1], newsize[0], newsize[1]);
1070 
1071     glOrtho(0, newsize[0], 0, newsize[1], -1.0f, 1.0f);
1072 
1073     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1074 
1075     // FIXME: I've noticed a problem with this approach. If there is
1076     // something in the window system obscuring part of the canvas
1077     // while this is called (as could e.g. happen with a size
1078     // indicator, as with the Sawfish window manager), the stencil
1079     // mask will not be set up for that part. 20041019 mortene.
1080     //
1081     // UPDATE 20041019 mortene: discussed this with pederb, and we
1082     // believe this may be due to a bug in either the OpenGL driver
1083     // (Nvidia 61.11, Linux) or window system or manager (Sawfish,
1084     // XFree86 v4.1.0.1). Should test on other systems to see if they
1085     // show the same artifact.
1086 
1087     glRasterPos2f(0, 0);
1088     glDrawPixels(newsize[0], newsize[1], GL_STENCIL_INDEX, GL_BITMAP,
1089                  PRIVATE(this)->stereostencilmask);
1090 
1091     glMatrixMode(GL_PROJECTION);
1092     glPopMatrix();
1093     glMatrixMode(GL_MODELVIEW);
1094     glPopMatrix();
1095   }
1096 }
1097 
1098 /*!
1099   Reinitialize after parameters affecting the OpenGL context has
1100   changed.
1101 */
1102 void
reinitialize(void)1103 SoRenderManager::reinitialize(void)
1104 {
1105   PRIVATE(this)->glaction->invalidateState();
1106 }
1107 
1108 /*!
1109   Redraw at first opportunity as system becomes idle.
1110 
1111   Multiple calls to this method before an actual redraw has taken
1112   place will only result in a single redraw of the scene.
1113 */
1114 void
scheduleRedraw(void)1115 SoRenderManager::scheduleRedraw(void)
1116 {
1117   PRIVATE(this)->lock();
1118   if (this->isActive() && PRIVATE(this)->rendercb) {
1119 #if COIN_DEBUG && 0 // debug
1120     SoDebugError::postInfo("SoRenderManager::scheduleRedraw",
1121                            "scheduling redrawshot (oneshotsensor) %p",
1122                            PRIVATE(this)->redrawshot);
1123 #endif // debug
1124     PRIVATE(this)->redrawshot->schedule();
1125   }
1126   PRIVATE(this)->unlock();
1127 }
1128 
1129 /*!
1130   Update window size of our SoGLRenderAction's viewport settings.
1131 
1132   Note that this will \e only change the information about window
1133   dimensions, the actual viewport size and origin (ie the rectangle
1134   which redraws are confined to) will stay the same.
1135 
1136   \sa setViewportRegion()
1137 */
1138 void
setWindowSize(const SbVec2s & newsize)1139 SoRenderManager::setWindowSize(const SbVec2s & newsize)
1140 {
1141 #if COIN_DEBUG && 0 // debug
1142   SoDebugError::postInfo("SoRenderManager::setWindowSize",
1143                          "(%d, %d)", newsize[0], newsize[1]);
1144 #endif // debug
1145 
1146   SbViewportRegion region = PRIVATE(this)->glaction->getViewportRegion();
1147   region.setWindowSize(newsize[0], newsize[1]);
1148   PRIVATE(this)->glaction->setViewportRegion(region);
1149 }
1150 
1151 /*!
1152   Returns the current render action window size.
1153 
1154   \sa setWindowSize()
1155 */
1156 const SbVec2s &
getWindowSize(void) const1157 SoRenderManager::getWindowSize(void) const
1158 {
1159   return PRIVATE(this)->glaction->getViewportRegion().getWindowSize();
1160 }
1161 
1162 /*!
1163   Set size of rendering area for the viewport within the current
1164   window.
1165 */
1166 void
setSize(const SbVec2s & newsize)1167 SoRenderManager::setSize(const SbVec2s & newsize)
1168 {
1169 #if COIN_DEBUG && 0 // debug
1170   SoDebugError::postInfo("SoRenderManager::setSize",
1171                          "(%d, %d)", newsize[0], newsize[1]);
1172 #endif // debug
1173 
1174   SbViewportRegion region = PRIVATE(this)->glaction->getViewportRegion();
1175   SbVec2s origin = region.getViewportOriginPixels();
1176   region.setViewportPixels(origin, newsize);
1177   PRIVATE(this)->glaction->setViewportRegion(region);
1178 }
1179 
1180 /*!
1181   Returns size of render area.
1182  */
1183 const SbVec2s &
getSize(void) const1184 SoRenderManager::getSize(void) const
1185 {
1186   return PRIVATE(this)->glaction->getViewportRegion().getViewportSizePixels();
1187 }
1188 
1189 /*!
1190   Set \e only the origin of the viewport region within the rendering
1191   window.
1192 
1193   \sa setViewportRegion(), setWindowSize()
1194 */
1195 void
setOrigin(const SbVec2s & newOrigin)1196 SoRenderManager::setOrigin(const SbVec2s & newOrigin)
1197 {
1198 #if COIN_DEBUG && 0 // debug
1199   SoDebugError::postInfo("SoRenderManager::setOrigin",
1200                          "(%d, %d)", newOrigin[0], newOrigin[1]);
1201 #endif // debug
1202 
1203   SbViewportRegion region = PRIVATE(this)->glaction->getViewportRegion();
1204   SbVec2s size = region.getViewportSizePixels();
1205   region.setViewportPixels(newOrigin, size);
1206   PRIVATE(this)->glaction->setViewportRegion(region);
1207 }
1208 
1209 /*!
1210   Returns origin of rendering area viewport.
1211 
1212   \sa setOrigin()
1213 */
1214 const SbVec2s &
getOrigin(void) const1215 SoRenderManager::getOrigin(void) const
1216 {
1217   return PRIVATE(this)->glaction->getViewportRegion().getViewportOriginPixels();
1218 }
1219 
1220 /*!
1221   Update our SoGLRenderAction's viewport settings.
1222 
1223   This will change \e both the information about window dimensions and
1224   the actual viewport size and origin.
1225 
1226   \sa setWindowSize()
1227 */
1228 void
setViewportRegion(const SbViewportRegion & newregion)1229 SoRenderManager::setViewportRegion(const SbViewportRegion & newregion)
1230 {
1231   PRIVATE(this)->glaction->setViewportRegion(newregion);
1232 }
1233 
1234 /*!
1235   Returns current viewport region used by the renderaction and the
1236   event handling.
1237 
1238   \sa setViewportRegion()
1239 */
1240 const SbViewportRegion &
getViewportRegion(void) const1241 SoRenderManager::getViewportRegion(void) const
1242 {
1243   return PRIVATE(this)->glaction->getViewportRegion();
1244 }
1245 
1246 /*!
1247   Sets color of rendering canvas.
1248  */
1249 void
setBackgroundColor(const SbColor4f & color)1250 SoRenderManager::setBackgroundColor(const SbColor4f & color)
1251 {
1252   PRIVATE(this)->backgroundcolor = color;
1253 }
1254 
1255 /*!
1256   Returns color used for clearing the rendering area before rendering
1257   the scene.
1258  */
1259 const SbColor4f &
getBackgroundColor(void) const1260 SoRenderManager::getBackgroundColor(void) const
1261 {
1262   return PRIVATE(this)->backgroundcolor;
1263 }
1264 
1265 /*!
1266   Sets color of overlay.
1267 */
1268 void
setOverlayColor(const SbColor4f & color)1269 SoRenderManager::setOverlayColor(const SbColor4f & color)
1270 {
1271   PRIVATE(this)->overlaycolor = color.getPackedValue();
1272   this->scheduleRedraw();
1273 }
1274 /*!
1275   Returns color used rendering overlay.
1276 */
1277 SbColor4f
getOverlayColor(void) const1278 SoRenderManager::getOverlayColor(void) const
1279 {
1280   SbColor4f retVal;
1281   retVal.setPackedValue(PRIVATE(this)->overlaycolor);
1282   return retVal;
1283 }
1284 
1285 /*!
1286   Set index of background color in the color lookup table if rendering
1287   in colorindex mode.
1288 
1289   Note: colorindex mode is not supported yet in Coin.
1290  */
1291 void
setBackgroundIndex(const int index)1292 SoRenderManager::setBackgroundIndex(const int index)
1293 {
1294   PRIVATE(this)->backgroundindex = index;
1295 }
1296 
1297 /*!
1298   Returns index of colormap for background filling.
1299 
1300   \sa setBackgroundIndex()
1301  */
1302 int
getBackgroundIndex(void) const1303 SoRenderManager::getBackgroundIndex(void) const
1304 {
1305   return PRIVATE(this)->backgroundindex;
1306 }
1307 
1308 /*!
1309   Turn RGB truecolor mode on or off. If you turn truecolor mode off,
1310   colorindex mode will be used instead.
1311 */
1312 void
setRGBMode(const SbBool yes)1313 SoRenderManager::setRGBMode(const SbBool yes)
1314 {
1315   PRIVATE(this)->isrgbmode = yes;
1316 }
1317 
1318 /*!
1319   Returns the "truecolor or colorindex" mode flag.
1320  */
1321 SbBool
isRGBMode(void) const1322 SoRenderManager::isRGBMode(void) const
1323 {
1324   return PRIVATE(this)->isrgbmode;
1325 }
1326 
1327 /*!
1328   Tell the scenemanager that double buffering is used
1329  */
1330 void
setDoubleBuffer(const SbBool enable)1331 SoRenderManager::setDoubleBuffer(const SbBool enable)
1332 {
1333   PRIVATE(this)->doublebuffer = enable;
1334 }
1335 
1336 /*!
1337   returns if the scenemanager is double buffered
1338  */
1339 SbBool
isDoubleBuffer(void) const1340 SoRenderManager::isDoubleBuffer(void) const
1341 {
1342   return PRIVATE(this)->doublebuffer;
1343 }
1344 
1345 /*!
1346   Set the callback function \a f to invoke when rendering the
1347   scene. \a userdata will be passed as the first argument of the
1348   function.
1349  */
1350 void
setRenderCallback(SoRenderManagerRenderCB * f,void * const userdata)1351 SoRenderManager::setRenderCallback(SoRenderManagerRenderCB * f,
1352                                   void * const userdata)
1353 {
1354   PRIVATE(this)->rendercb = f;
1355   PRIVATE(this)->rendercbdata = userdata;
1356 }
1357 
1358 /*!
1359   Activate rendering and event handling. Default is \c off.
1360  */
1361 void
activate(void)1362 SoRenderManager::activate(void)
1363 {
1364   PRIVATE(this)->isactive = TRUE;
1365 }
1366 
1367 /*!
1368   Deactive rendering and event handling.
1369  */
1370 void
deactivate(void)1371 SoRenderManager::deactivate(void)
1372 {
1373   PRIVATE(this)->isactive = FALSE;
1374 }
1375 
1376 /*!
1377   Returns the \e active flag.
1378  */
1379 int
isActive(void) const1380 SoRenderManager::isActive(void) const
1381 {
1382   return PRIVATE(this)->isactive;
1383 }
1384 
1385 /*!
1386   Do an immediate redraw by calling the redraw callback function.
1387  */
1388 void
redraw(void)1389 SoRenderManager::redraw(void)
1390 {
1391   if (PRIVATE(this)->rendercb) {
1392     PRIVATE(this)->rendercb(PRIVATE(this)->rendercbdata, this);
1393   }
1394 }
1395 
1396 /*!
1397   Returns \c TRUE if the SoRenderManager automatically redraws the
1398   scene upon detecting changes in the scene graph.
1399 
1400   The automatic redraw is turned on and off by setting either a valid
1401   callback function with setRenderCallback(), or by passing \c NULL.
1402  */
1403 SbBool
isAutoRedraw(void) const1404 SoRenderManager::isAutoRedraw(void) const
1405 {
1406   return PRIVATE(this)->rendercb != NULL;
1407 }
1408 
1409 
1410 /*!
1411   Sets the render mode.
1412 */
1413 void
setRenderMode(const RenderMode mode)1414 SoRenderManager::setRenderMode(const RenderMode mode)
1415 {
1416   PRIVATE(this)->rendermode = mode;
1417   this->scheduleRedraw();
1418   PRIVATE(this)->dummynode->touch();
1419 }
1420 
1421 /*!
1422   Returns the current render mode.
1423 */
1424 SoRenderManager::RenderMode
getRenderMode(void) const1425 SoRenderManager::getRenderMode(void) const
1426 {
1427   return PRIVATE(this)->rendermode;
1428 }
1429 
1430 /*!
1431   Sets the stereo mode.
1432 */
1433 void
setStereoMode(const StereoMode mode)1434 SoRenderManager::setStereoMode(const StereoMode mode)
1435 {
1436   PRIVATE(this)->stereomode = mode;
1437   this->scheduleRedraw();
1438   PRIVATE(this)->dummynode->touch();
1439 }
1440 
1441 /*!
1442   Returns the current stereo mode.
1443 */
1444 SoRenderManager::StereoMode
getStereoMode(void) const1445 SoRenderManager::getStereoMode(void) const
1446 {
1447   return PRIVATE(this)->stereomode;
1448 }
1449 
1450 /*!
1451   Sets the stereo offset used when doing stereo rendering.
1452 */
1453 void
setStereoOffset(const float offset)1454 SoRenderManager::setStereoOffset(const float offset)
1455 {
1456   PRIVATE(this)->stereooffset = offset;
1457   this->scheduleRedraw();
1458   PRIVATE(this)->dummynode->touch();
1459 }
1460 
1461 /*!
1462   Returns the current stereo offset.
1463 */
1464 float
getStereoOffset(void) const1465 SoRenderManager::getStereoOffset(void) const
1466 {
1467   return PRIVATE(this)->stereooffset;
1468 }
1469 
1470 /*!
1471   Turn antialiased rendering on or off.
1472 
1473   See documentation for SoGLRenderAction::setSmoothing() and
1474   SoGLRenderAction::setNumPasses().
1475  */
1476 void
setAntialiasing(const SbBool smoothing,const int numpasses)1477 SoRenderManager::setAntialiasing(const SbBool smoothing, const int numpasses)
1478 {
1479   PRIVATE(this)->glaction->setSmoothing(smoothing);
1480   PRIVATE(this)->glaction->setNumPasses(numpasses);
1481   this->scheduleRedraw();
1482 }
1483 
1484 /*!
1485   Returns rendering pass information.
1486 
1487   \sa setAntialiasing()
1488  */
1489 void
getAntialiasing(SbBool & smoothing,int & numpasses) const1490 SoRenderManager::getAntialiasing(SbBool & smoothing, int & numpasses) const
1491 {
1492   smoothing = PRIVATE(this)->glaction->isSmoothing();
1493   numpasses = PRIVATE(this)->glaction->getNumPasses();
1494 }
1495 
1496 /*!
1497   Set the \a action to use for rendering. Overrides the default action
1498   made in the constructor.
1499  */
1500 void
setGLRenderAction(SoGLRenderAction * const action)1501 SoRenderManager::setGLRenderAction(SoGLRenderAction * const action)
1502 {
1503   SbBool haveregion = FALSE;
1504   SbViewportRegion region;
1505   if (PRIVATE(this)->glaction) { // remember existing viewport region
1506     region = PRIVATE(this)->glaction->getViewportRegion();
1507     haveregion = TRUE;
1508   }
1509   if (PRIVATE(this)->deleteglaction) {
1510     delete PRIVATE(this)->glaction;
1511     PRIVATE(this)->glaction = NULL;
1512   }
1513 
1514   // If action change, we need to invalidate state to enable lazy GL
1515   // elements to be evaluated correctly.
1516   //
1517   // Note that the SGI and TGS Inventor implementations doesn't do
1518   // this -- which smells of a bug.
1519   if (action && action != PRIVATE(this)->glaction) action->invalidateState();
1520   PRIVATE(this)->glaction = action;
1521   PRIVATE(this)->deleteglaction = FALSE;
1522   if (PRIVATE(this)->glaction && haveregion)
1523     PRIVATE(this)->glaction->setViewportRegion(region);
1524 }
1525 
1526 /*!
1527   Returns pointer to render action.
1528  */
1529 SoGLRenderAction *
getGLRenderAction(void) const1530 SoRenderManager::getGLRenderAction(void) const
1531 {
1532   return PRIVATE(this)->glaction;
1533 }
1534 
1535 /*!
1536   This method returns the current autoclipping strategy.
1537 
1538   \sa setAutoClipping
1539 */
1540 
1541 SoRenderManager::AutoClippingStrategy
getAutoClipping(void) const1542 SoRenderManager::getAutoClipping(void) const
1543 {
1544   return PRIVATE(this)->autoclipping;
1545 }
1546 
1547 /*!
1548   When the SoRenderManager::FIXED_NEAR_PLANE autoclipping strategy is
1549   used, you set the value of the near plane distance with this method.
1550 
1551   \sa setAutoClipping, getNearPlaneValue, SoRenderManager::AutoClippingStrategy
1552 */
1553 
1554 void
setNearPlaneValue(float value)1555 SoRenderManager::setNearPlaneValue(float value)
1556 {
1557   PRIVATE(this)->nearplanevalue = value;
1558 }
1559 
1560 /*!
1561   This method returns the near plane distance value that will be used
1562   when the SoRenderManager::FIXED_NEAR_PLANE autoclipping strategy is used.
1563 
1564   Default value is 0.6.
1565 
1566   \sa setAutoClipping, setNearPlaneValue,  SoRenderManager::AutoClippingStrategy
1567 */
1568 
1569 float
getNearPlaneValue(void) const1570 SoRenderManager::getNearPlaneValue(void) const
1571 {
1572   return PRIVATE(this)->nearplanevalue;
1573 }
1574 
1575 /*!
1576   Enable/disable textures when rendering.
1577   Defaults to TRUE.
1578 
1579   \sa isTexturesEnabled
1580 */
1581 
1582 void
setTexturesEnabled(const SbBool onoff)1583 SoRenderManager::setTexturesEnabled(const SbBool onoff)
1584 {
1585   PRIVATE(this)->texturesenabled = onoff;
1586 }
1587 
1588 /*!
1589   Returns whether textures are enabled or not.
1590 
1591   \sa setTexturesEnabled
1592 */
1593 
1594 SbBool
isTexturesEnabled(void) const1595 SoRenderManager::isTexturesEnabled(void) const
1596 {
1597   return PRIVATE(this)->texturesenabled;
1598 }
1599 
1600 /*!
1601   Set up the redraw \a priority for the SoOneShotSensor used to
1602   trigger redraws. By setting this lower than for your own sensors,
1603   you can make sure some code is always run before redraw happens.
1604 
1605   \sa SoDelayQueueSensor
1606  */
1607 void
setRedrawPriority(const uint32_t priority)1608 SoRenderManager::setRedrawPriority(const uint32_t priority)
1609 {
1610   PRIVATE(this)->redrawpri = priority;
1611 
1612   if (PRIVATE(this)->redrawshot) PRIVATE(this)->redrawshot->setPriority(priority);
1613   if (PRIVATE(this)->rootsensor) PRIVATE(this)->rootsensor->setPriority(PRIVATE(this)->redrawpri == 0 ? 0 : 1);
1614 }
1615 
1616 /*!
1617   Returns value of priority on the redraw sensor.
1618  */
1619 uint32_t
getRedrawPriority(void) const1620 SoRenderManager::getRedrawPriority(void) const
1621 {
1622   return PRIVATE(this)->redrawpri;
1623 }
1624 
1625 /*!
1626   Set the \a action to use for rendering audio. Overrides the default action
1627   made in the constructor.
1628  */
1629 void
setAudioRenderAction(SoAudioRenderAction * const action)1630 SoRenderManager::setAudioRenderAction(SoAudioRenderAction * const action)
1631 {
1632   if (PRIVATE(this)->deleteaudiorenderaction) {
1633     delete PRIVATE(this)->audiorenderaction;
1634     PRIVATE(this)->audiorenderaction = NULL;
1635   }
1636 
1637   // If action change, we need to invalidate state to enable lazy GL
1638   // elements to be evaluated correctly.
1639   //
1640   if (action && action != PRIVATE(this)->audiorenderaction) action->invalidateState();
1641   PRIVATE(this)->audiorenderaction = action;
1642   PRIVATE(this)->deleteaudiorenderaction = FALSE;
1643 }
1644 
1645 /*!
1646   Returns pointer to audio render action.
1647  */
1648 SoAudioRenderAction *
getAudioRenderAction(void) const1649 SoRenderManager::getAudioRenderAction(void) const
1650 {
1651   return PRIVATE(this)->audiorenderaction;
1652 }
1653 
1654 /*!
1655   Returns the default priority of the redraw sensor.
1656 
1657   \sa SoDelayQueueSensor, setRedrawPriority()
1658  */
1659 uint32_t
getDefaultRedrawPriority(void)1660 SoRenderManager::getDefaultRedrawPriority(void)
1661 {
1662   return 10000;
1663 }
1664 
1665 /*!
1666   Set whether or not for SoRenderManager instances to "touch" the
1667   global \c realTime field after a redraw. If this is not done,
1668   redrawing when animating the scene will only happen as fast as the
1669   \c realTime interval goes (which defaults to 12 times a second).
1670 
1671   \sa SoDB::setRealTimeInterval()
1672  */
1673 void
enableRealTimeUpdate(const SbBool flag)1674 SoRenderManager::enableRealTimeUpdate(const SbBool flag)
1675 {
1676   SoRenderManagerP::touchtimer = flag;
1677   if (!SoRenderManagerP::cleanupfunctionset) {
1678     coin_atexit((coin_atexit_f*) SoRenderManagerP::cleanup, CC_ATEXIT_NORMAL);
1679     SoRenderManagerP::cleanupfunctionset = TRUE;
1680   }
1681 }
1682 
1683 /*!
1684   Returns whether or not we automatically notifies everything
1685   connected to the \c realTime field after a redraw.
1686  */
1687 SbBool
isRealTimeUpdateEnabled(void)1688 SoRenderManager::isRealTimeUpdateEnabled(void)
1689 {
1690   return SoRenderManagerP::touchtimer;
1691 }
1692 
1693 
1694 /*!
1695   Adds a function to be called before rendering starts
1696 
1697   \param[in] cb function to be called
1698   \param[in] data User specified data to supply to callback function
1699 */
1700 void
addPreRenderCallback(SoRenderManagerRenderCB * cb,void * data)1701 SoRenderManager::addPreRenderCallback(SoRenderManagerRenderCB * cb, void * data)
1702 {
1703   PRIVATE(this)->preRenderCallbacks.push_back(SoRenderManagerP::RenderCBTouple(cb, data));
1704 }
1705 
1706 
1707 /*!
1708   Removes a prerendercallback.
1709 
1710   \pre The tuple (cb, data) must exactly match an earlier call to
1711   SoRenderManager::addPreRenderCallback
1712 
1713   \param[in] cb function to be called
1714   \param[in] data User specified data to supply to callback function
1715 */
1716 void
removePreRenderCallback(SoRenderManagerRenderCB * cb,void * data)1717 SoRenderManager::removePreRenderCallback(SoRenderManagerRenderCB * cb, void * data)
1718 {
1719   std::vector<SoRenderManagerP::RenderCBTouple>::iterator findit =
1720     std::find(PRIVATE(this)->preRenderCallbacks.begin(),
1721          PRIVATE(this)->preRenderCallbacks.end(),
1722          SoRenderManagerP::RenderCBTouple(cb, data));
1723   assert(
1724         (findit != PRIVATE(this)->preRenderCallbacks.end())
1725         &&
1726         "Tried to remove a cb,data tuple which doesn't exist"
1727         );
1728   PRIVATE(this)->preRenderCallbacks.erase(findit);
1729 }
1730 
1731 /*!
1732   Adds a function to be called after rendering is complete
1733 
1734   \param[in] cb function to be called
1735   \param[in] data User specified data to supply to callback function
1736 */
1737 void
addPostRenderCallback(SoRenderManagerRenderCB * cb,void * data)1738 SoRenderManager::addPostRenderCallback(SoRenderManagerRenderCB * cb, void * data)
1739 {
1740   PRIVATE(this)->postRenderCallbacks.push_back(SoRenderManagerP::RenderCBTouple(cb, data));
1741 }
1742 
1743 /*!
1744   Removes a postrendercallback.
1745 
1746   \pre The tuple (cb, data) must exactly match an earlier call to
1747   SoRenderManager::addPostRenderCallback
1748 
1749   \param[in] cb function to be called
1750   \param[in] data User specified data to supply to callback function
1751 */
1752 void
removePostRenderCallback(SoRenderManagerRenderCB * cb,void * data)1753 SoRenderManager::removePostRenderCallback(SoRenderManagerRenderCB * cb, void * data)
1754 {
1755   std::vector<SoRenderManagerP::RenderCBTouple>::iterator findit =
1756     std::find(PRIVATE(this)->postRenderCallbacks.begin(),
1757          PRIVATE(this)->postRenderCallbacks.end(),
1758          SoRenderManagerP::RenderCBTouple(cb, data));
1759   assert(
1760         (findit != PRIVATE(this)->postRenderCallbacks.end())
1761         &&
1762         "Tried to remove a cb,data tuple which doesn't exist"
1763         );
1764   PRIVATE(this)->postRenderCallbacks.erase(findit);
1765 }
1766 
1767 #undef PRIVATE
1768 #undef PUBLIC
1769