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 #include <Inventor/nodes/SoSceneTextureCubeMap.h>
34 #include "coindefs.h"
35 #include <Inventor/SoInput.h>
36 #include <Inventor/nodes/SoSubNode.h>
37 #include <Inventor/actions/SoCallbackAction.h>
38 #include <Inventor/actions/SoGLRenderAction.h>
39 #include <Inventor/actions/SoRayPickAction.h>
40 #include <Inventor/actions/SoSearchAction.h>
41 #include <Inventor/elements/SoTextureQualityElement.h>
42 #include <Inventor/elements/SoTextureOverrideElement.h>
43 #include <Inventor/elements/SoGLLazyElement.h>
44 #include <Inventor/elements/SoCacheElement.h>
45 #include <Inventor/elements/SoGLCacheContextElement.h>
46 #include <Inventor/elements/SoTextureUnitElement.h>
47 #include <Inventor/elements/SoGLMultiTextureImageElement.h>
48 #include <Inventor/elements/SoGLMultiTextureEnabledElement.h>
49 #include <Inventor/elements/SoShapeStyleElement.h>
50 #include <Inventor/errors/SoReadError.h>
51 #include <Inventor/misc/SoGLCubeMapImage.h>
52 #include <Inventor/sensors/SoFieldSensor.h>
53 #include <Inventor/lists/SbStringList.h>
54 #include <Inventor/errors/SoDebugError.h>
55 #include <Inventor/nodes/SoSeparator.h>
56 #include <Inventor/nodes/SoCamera.h>
57 #include <Inventor/nodes/SoOrthographicCamera.h>
58 #include <Inventor/nodes/SoPerspectiveCamera.h>
59 #include <Inventor/SbImage.h>
60 #include <Inventor/C/glue/gl.h>
61 
62 #ifdef HAVE_CONFIG_H
63 #include <config.h>
64 #endif // HAVE_CONFIG_H
65 
66 #ifdef COIN_THREADSAFE
67 #include <Inventor/threads/SbMutex.h>
68 #endif // COIN_THREADSAFE
69 
70 // *************************************************************************
71 
72 /*!
73   \class SoSceneTextureCubeMap SoSceneTextureCubeMap.h Inventor/nodes/SoSceneTextureCubeMap.h
74   \brief Renders a scene into a texture cube map.
75 
76   \ingroup nodes
77   \since Coin 2.5
78 */
79 
80 // FIXME: more detailed description on how the camera is to be set 20050429 martin
81 // if scene does not contain a camera, a default camera is inserted into the
82 // cachedScene (the original scene stays untouched!)
83 // Default camera = SoPerspectiveCamera {
84 //   position SbVec3f(0 0 0)
85 //   rotation SbRotation(SbVec3f(0,1,0), 0.0f)
86 //   heightAngle (M_PI / 2.0f)
87 //   nearDistance = 0.1;
88 //   farDistance = 100;
89 // }
90 
91 class SoSceneTextureCubeMapP {
92  public:
93   SoSceneTextureCubeMapP(SoSceneTextureCubeMap * theAPI);
94   ~SoSceneTextureCubeMapP();
95 
96   SoSceneTextureCubeMap * api;
97   void * glcontext;
98   SbVec2s glcontextsize;
99   int contextid;
100 
101   SoGLRenderAction * glaction;
102   SoGLCubeMapImage * glimage;
103   SbBool pbuffervalid;
104   SbBool glimagevalid;
105   SbBool glrectangle;
106 
107   SoNode   * cachedScene;  // scene with guaranteed camera
108   SoCamera * cachedCamera; // reference to the camera
109 
110   void updatePBuffer(SoState * state, const float quality);
111   static void prerendercb(void * userdata, SoGLRenderAction * action);
112 
113   SoCamera * findCamera(void);
114   void destroyCamera(void);
115   SoCamera * ensureCamera(void);
116   SoNode   * updateCamera(const SoGLCubeMapImage::Target target);
117 
118 #ifdef COIN_THREADSAFE
119   SbMutex mutex;
120 #endif // COIN_THREADSAFE
121 
122   SbBool canrendertotexture;
123   unsigned char * offscreenbuffer;
124   int offscreenbuffersize;
125   SbBool hadSceneCamera;
126   SbBool hasSceneChanged;
127 
128   // FIXME: this will not work on all platforms/compilers
129   static SbRotation ROT_NEG_X;
130   static SbRotation ROT_POS_X;
131   static SbRotation ROT_NEG_Y;
132   static SbRotation ROT_POS_Y;
133   static SbRotation ROT_NEG_Z;
134   static SbRotation ROT_POS_Z;
135 };
136 
137 
138 SbRotation SoSceneTextureCubeMapP::ROT_NEG_X =
139   SbRotation(SbVec3f(0,1,0), (float) (M_PI/2.0f)) *
140   SbRotation(SbVec3f(1,0,0), (float) M_PI);
141 SbRotation SoSceneTextureCubeMapP::ROT_POS_X =
142   SbRotation(SbVec3f(0,1,0), (float) (M_PI/-2.0f)) *
143   SbRotation(SbVec3f(1,0,0), (float) M_PI);
144 SbRotation SoSceneTextureCubeMapP::ROT_NEG_Y =
145   SbRotation(SbVec3f(1,0,0), (float) (M_PI / -2.0f));
146 SbRotation SoSceneTextureCubeMapP::ROT_POS_Y =
147   SbRotation(SbVec3f(1,0,0), (float) (M_PI / 2.0f));
148 SbRotation SoSceneTextureCubeMapP::ROT_NEG_Z =
149   SbRotation(SbVec3f(0,1,0), 0.0f) *
150   SbRotation(SbVec3f(0,0,1), (float) M_PI);
151 SbRotation SoSceneTextureCubeMapP::ROT_POS_Z =
152   SbRotation(SbVec3f(0,1,0), (float) M_PI) *
153   SbRotation(SbVec3f(0,0,1), (float) M_PI);
154 
155 #define PRIVATE(p) (p->pimpl)
156 
157 #ifdef COIN_THREADSAFE
158 #define LOCK_GLIMAGE(_thisp_) (PRIVATE(_thisp_)->mutex.lock())
159 #define UNLOCK_GLIMAGE(_thisp_) (PRIVATE(_thisp_)->mutex.unlock())
160 #else // COIN_THREADSAFE
161 #define LOCK_GLIMAGE(_thisp_)
162 #define UNLOCK_GLIMAGE(_thisp_)
163 #endif // COIN_THREADSAFE
164 
165 
166 SO_NODE_SOURCE(SoSceneTextureCubeMap);
167 
168 static SoGLCubeMapImage::Wrap
translateWrap(const SoSceneTextureCubeMap::Wrap wrap)169 translateWrap(const SoSceneTextureCubeMap::Wrap wrap)
170 {
171   if (wrap == SoSceneTextureCubeMap::REPEAT) return SoGLImage::REPEAT;
172   return SoGLImage::CLAMP;
173 }
174 
175 /*!
176   Constructor.
177 */
SoSceneTextureCubeMap(void)178 SoSceneTextureCubeMap::SoSceneTextureCubeMap(void)
179 {
180   PRIVATE(this) = new SoSceneTextureCubeMapP(this);
181 
182   SO_NODE_CONSTRUCTOR(SoSceneTextureCubeMap);
183 
184   SO_NODE_ADD_FIELD(size, (256, 256));
185   SO_NODE_ADD_FIELD(scene, (NULL));
186   SO_NODE_ADD_FIELD(backgroundColor, (0.0f, 0.0f, 0.0f));
187   SO_NODE_ADD_FIELD(transparencyFunction, (NONE));
188 
189   SO_NODE_ADD_FIELD(wrapS, (REPEAT));
190   SO_NODE_ADD_FIELD(wrapT, (REPEAT));
191   SO_NODE_ADD_FIELD(wrapR, (REPEAT));
192   SO_NODE_ADD_FIELD(model, (MODULATE));
193   SO_NODE_ADD_FIELD(blendColor, (0.0f, 0.0f, 0.0f));
194 
195   SO_NODE_DEFINE_ENUM_VALUE(Wrap, REPEAT);
196   SO_NODE_DEFINE_ENUM_VALUE(Wrap, CLAMP);
197 
198   SO_NODE_SET_SF_ENUM_TYPE(wrapS, Wrap);
199   SO_NODE_SET_SF_ENUM_TYPE(wrapT, Wrap);
200   SO_NODE_SET_SF_ENUM_TYPE(wrapR, Wrap);
201 
202   SO_NODE_DEFINE_ENUM_VALUE(Model, MODULATE);
203   SO_NODE_DEFINE_ENUM_VALUE(Model, DECAL);
204   SO_NODE_DEFINE_ENUM_VALUE(Model, BLEND);
205   SO_NODE_DEFINE_ENUM_VALUE(Model, REPLACE);
206   SO_NODE_SET_SF_ENUM_TYPE(model, Model);
207 
208   SO_NODE_DEFINE_ENUM_VALUE(TransparencyFunction, NONE);
209   SO_NODE_DEFINE_ENUM_VALUE(TransparencyFunction, ALPHA_BLEND);
210   SO_NODE_DEFINE_ENUM_VALUE(TransparencyFunction, ALPHA_TEST);
211   SO_NODE_SET_SF_ENUM_TYPE(transparencyFunction, TransparencyFunction);
212 }
213 
214 /*!
215   Destructor. Frees up internal resources used to store texture image
216   data.
217 */
~SoSceneTextureCubeMap()218 SoSceneTextureCubeMap::~SoSceneTextureCubeMap()
219 {
220   delete PRIVATE(this);
221 }
222 
223 // Documented in superclass.
224 void
initClass(void)225 SoSceneTextureCubeMap::initClass(void)
226 {
227   SO_NODE_INIT_CLASS(SoSceneTextureCubeMap, SoNode, "Node");
228 
229   SO_ENABLE(SoGLRenderAction, SoGLMultiTextureImageElement);
230   SO_ENABLE(SoGLRenderAction, SoGLMultiTextureEnabledElement);
231 
232   SO_ENABLE(SoCallbackAction, SoMultiTextureImageElement);
233   SO_ENABLE(SoCallbackAction, SoMultiTextureEnabledElement);
234 
235   SO_ENABLE(SoRayPickAction, SoMultiTextureImageElement);
236   SO_ENABLE(SoRayPickAction, SoMultiTextureEnabledElement);
237 }
238 
239 // Documented in superclass.
240 void
GLRender(SoGLRenderAction * action)241 SoSceneTextureCubeMap::GLRender(SoGLRenderAction * action)
242 {
243   SoState * state = action->getState();
244 
245   if (SoTextureOverrideElement::getImageOverride(state))
246     return;
247 
248   float quality = SoTextureQualityElement::get(state);
249 
250   const cc_glglue * glue = cc_glglue_instance(SoGLCacheContextElement::get(state));
251   SoNode * root = this->scene.getValue();
252 
253   LOCK_GLIMAGE(this);
254 
255   if (root && (!PRIVATE(this)->glimagevalid || !PRIVATE(this)->pbuffervalid)) {
256     PRIVATE(this)->updatePBuffer(state, quality);
257 
258     // don't cache when we change the glimage
259     SoCacheElement::setInvalid(TRUE);
260     if (state->isCacheOpen()) {
261       SoCacheElement::invalidate(state);
262     }
263   }
264   UNLOCK_GLIMAGE(this);
265 
266   SoMultiTextureImageElement::Model glmodel = (SoMultiTextureImageElement::Model)
267     this->model.getValue();
268 
269   if (glmodel == SoMultiTextureImageElement::REPLACE) {
270     if (!cc_glglue_glversion_matches_at_least(glue, 1, 1, 0)) {
271       static int didwarn = 0;
272       if (!didwarn) {
273         SoDebugError::postWarning("SoSceneTextureCubeMap::GLRender",
274                                   "Unable to use the GL_REPLACE texture model. "
275                                   "Your OpenGL version is < 1.1. "
276                                   "Using GL_MODULATE instead.");
277         didwarn = 1;
278       }
279       // use MODULATE and not DECAL, since DECAL only works for RGB
280       // and RGBA textures
281       glmodel = SoMultiTextureImageElement::MODULATE;
282     }
283   }
284 
285   int unit = SoTextureUnitElement::get(state);
286   int maxunits = cc_glglue_max_texture_units(glue);
287   if (unit < maxunits) {
288     SoGLMultiTextureImageElement::set(state, this, unit,
289                                       PRIVATE(this)->glimage,
290                                       glmodel,
291                                       this->blendColor.getValue());
292     if (quality > 0.0f && PRIVATE(this)->glimagevalid) {
293       SoGLMultiTextureEnabledElement::enableCubeMap(state, this, unit);
294     }
295   }
296   else {
297     // we already warned in SoTextureUnit. I think it's best to just
298     // ignore the texture here so that all texture for non-supported
299     // units will be ignored. pederb, 2003-11-04
300   }
301 }
302 
303 // Documented in superclass.
304 void
doAction(SoAction * COIN_UNUSED_ARG (action))305 SoSceneTextureCubeMap::doAction(SoAction * COIN_UNUSED_ARG(action))
306 {
307   // not implemented yet
308 }
309 
310 // doc from parent
311 void
callback(SoCallbackAction * action)312 SoSceneTextureCubeMap::callback(SoCallbackAction * action)
313 {
314   SoSceneTextureCubeMap::doAction(action);
315 }
316 
317 // doc from parent
318 void
rayPick(SoRayPickAction * action)319 SoSceneTextureCubeMap::rayPick(SoRayPickAction * action)
320 {
321   SoSceneTextureCubeMap::doAction(action);
322 }
323 
324 // Documented in superclass. Overridden to detect when fields change.
325 void
notify(SoNotList * list)326 SoSceneTextureCubeMap::notify(SoNotList * list)
327 {
328   SoField * f = list->getLastField();
329   if (f == &this->scene) {
330     PRIVATE(this)->hasSceneChanged = TRUE; // refetch camera and scene
331     PRIVATE(this)->pbuffervalid = FALSE; // rerender scene
332   }
333   else if (f == &this->size) {
334     PRIVATE(this)->pbuffervalid = FALSE; // rerender scene
335   }
336   else if (f == &this->wrapS || f == &this->wrapT || f == &this->wrapR ||
337            f == &this->model || f == &this->transparencyFunction) {
338     // no need to render scene again, but update the texture object
339     PRIVATE(this)->glimagevalid = FALSE;
340   }
341   inherited::notify(list);
342 }
343 
344 /* *********************************************************************** */
345 /* ***                       private implementation                    *** */
346 /* *********************************************************************** */
347 
348 #define PUBLIC(p) (p->api)
349 
SoSceneTextureCubeMapP(SoSceneTextureCubeMap * apiptr)350 SoSceneTextureCubeMapP::SoSceneTextureCubeMapP(SoSceneTextureCubeMap * apiptr)
351 {
352   this->api = apiptr;
353   this->glimage = NULL;
354   this->glimagevalid = FALSE;
355   this->glcontext = NULL;
356   this->pbuffervalid = FALSE;
357   this->glaction = NULL;
358   this->glcontextsize.setValue(-1,-1);
359   this->glrectangle = FALSE;
360   this->offscreenbuffer = NULL;
361   this->offscreenbuffersize = 0;
362   this->canrendertotexture = FALSE;
363   this->contextid = -1;
364   this->cachedScene = NULL;
365   this->cachedCamera = NULL;
366   this->hadSceneCamera = FALSE;
367   this->hasSceneChanged = TRUE;
368 }
369 
~SoSceneTextureCubeMapP()370 SoSceneTextureCubeMapP::~SoSceneTextureCubeMapP()
371 {
372   if (this->glimage) this->glimage->unref(NULL);
373   this->destroyCamera();
374   if (this->glcontext != NULL) {
375     cc_glglue_context_destruct(this->glcontext);
376   }
377   delete[] this->offscreenbuffer;
378   delete this->glaction;
379 }
380 
381 void
updatePBuffer(SoState * state,const float quality)382 SoSceneTextureCubeMapP::updatePBuffer(SoState * state, const float quality)
383 {
384   SbVec2s size = PUBLIC(this)->size.getValue();
385 
386   assert(PUBLIC(this)->scene.getValue());
387 
388   if ((this->glcontext && this->glcontextsize != size) ||
389       (size == SbVec2s(0,0))) {
390     if (this->glimage) {
391       this->glimage->unref(state);
392       this->glimage = NULL;
393     }
394     if (this->glcontext) {
395       cc_glglue_context_destruct(this->glcontext);
396       this->glcontextsize.setValue(-1,-1);
397       this->glcontext = NULL;
398     }
399     delete this->glaction;
400     this->glaction = NULL;
401     this->glimagevalid = FALSE;
402   }
403   if (size == SbVec2s(0,0)) return;
404 
405   // FIXME: temporary until non power of two textures are supported,
406   // pederb 2003-12-05
407   size[0] = (short) coin_geq_power_of_two(size[0]);
408   size[1] = (short) coin_geq_power_of_two(size[1]);
409 
410   if (this->glcontext == NULL) {
411     this->glcontextsize = size;
412     // disabled until an pbuffer extension is available to create a
413     // render-to-texture pbuffer that has a non power of two size.
414     // pederb, 2003-12-05
415     if (1) { // if (!glue->has_ext_texture_rectangle) {
416       this->glcontextsize[0] = (short) coin_geq_power_of_two(size[0]);
417       this->glcontextsize[1] = (short) coin_geq_power_of_two(size[1]);
418 
419       if (this->glcontextsize != size) {
420         static int didwarn = 0;
421         if (!didwarn) {
422           SoDebugError::postWarning("SoSceneTextureCubeMapP::updatePBuffer",
423                                     "Requested non power of two size, "
424                                     "but your OpenGL driver lacks support "
425                                     "for such pbuffer textures.");
426           didwarn = 1;
427         }
428       }
429     }
430 
431     this->glrectangle = FALSE;
432     if (!coin_is_power_of_two(this->glcontextsize[0]) ||
433         !coin_is_power_of_two(this->glcontextsize[1])) {
434       // we only get here if the OpenGL driver can handle non power of
435       // two textures/pbuffers.
436       this->glrectangle = TRUE;
437     }
438 
439     // FIXME: make it possible to specify what kind of context you want
440     // (RGB or RGBA, I guess). pederb, 2003-11-27
441     unsigned int x = this->glcontextsize[0];
442     unsigned int y = this->glcontextsize[1];
443 
444     this->glcontext = cc_glglue_context_create_offscreen(x, y);
445     this->canrendertotexture =
446       cc_glglue_context_can_render_to_texture(this->glcontext);
447 
448     if (!this->glaction) {
449       this->contextid = (int)SoGLCacheContextElement::getUniqueCacheContext();
450       this->glaction =
451         new SoGLRenderAction(SbViewportRegion(this->glcontextsize));
452       this->glaction->
453         addPreRenderCallback(SoSceneTextureCubeMapP::prerendercb,
454                              (void*) PUBLIC(this));
455     }
456     else {
457       this->glaction->
458         setViewportRegion(SbViewportRegion(this->glcontextsize));
459     }
460     this->glaction->setCacheContext(this->contextid);
461     this->glimagevalid = FALSE;
462   }
463 
464   if (!this->pbuffervalid) {
465     assert(this->glaction != NULL);
466     assert(this->glcontext != NULL);
467     this->glaction->setTransparencyType((SoGLRenderAction::TransparencyType)
468                                         SoShapeStyleElement::getTransparencyType(state));
469 
470     cc_glglue_context_make_current(this->glcontext);
471 
472 
473     glEnable(GL_DEPTH_TEST);
474 
475     if (!this->canrendertotexture) {
476       SbVec2s size = this->glcontextsize;
477       int cubeSideSize = size[0]*size[1]*4;
478       int reqbytes = cubeSideSize*6; // 6 cube sides
479       if (reqbytes > this->offscreenbuffersize) {
480         delete[] this->offscreenbuffer;
481         this->offscreenbuffer = new unsigned char[reqbytes];
482         this->offscreenbuffersize = reqbytes;
483       }
484 
485       unsigned char * cubeSidePtr = this->offscreenbuffer;
486 
487       for (int i=0; i<6; i++) {
488         this->glaction->apply(this->updateCamera((SoGLCubeMapImage::Target)i));
489         glFlush();
490 
491         glPixelStorei(GL_PACK_ALIGNMENT, 1);
492         glReadPixels(0,0,size[0],size[1],GL_RGBA,GL_UNSIGNED_BYTE,cubeSidePtr);
493         glPixelStorei(GL_PACK_ALIGNMENT, 4);
494         cubeSidePtr += cubeSideSize;
495       }
496     }
497 
498     cc_glglue_context_reinstate_previous(this->glcontext);
499   }
500 
501   if (!this->glimagevalid || (this->glimage == NULL)) {
502     // just delete old glimage
503     if (this->glimage) {
504       this->glimage->unref(state);
505       this->glimage = NULL;
506     }
507     this->glimage = new SoGLCubeMapImage;
508     uint32_t flags = this->glimage->getFlags();
509     if (this->glrectangle) {
510       flags |= SoGLImage::RECTANGLE;
511     }
512     switch ((SoSceneTextureCubeMap::TransparencyFunction) (PUBLIC(this)->transparencyFunction.getValue())) {
513     case SoSceneTextureCubeMap::NONE:
514       flags |= SoGLImage::FORCE_TRANSPARENCY_FALSE|SoGLImage::FORCE_ALPHA_TEST_FALSE;
515       break;
516     case SoSceneTextureCubeMap::ALPHA_TEST:
517       flags |= SoGLImage::FORCE_TRANSPARENCY_TRUE|SoGLImage::FORCE_ALPHA_TEST_TRUE;
518       break;
519     case SoSceneTextureCubeMap::ALPHA_BLEND:
520       flags |= SoGLImage::FORCE_TRANSPARENCY_TRUE|SoGLImage::FORCE_ALPHA_TEST_FALSE;
521       break;
522     default:
523       assert(0 && "should not get here");
524       break;
525     }
526     this->glimage->setFlags(flags);
527 
528     if (this->canrendertotexture) {
529       // FIXME: not implemented yet - 20050427 martin
530 
531       // bind texture to pbuffer
532       this->glimage->setPBuffer(state, this->glcontext,
533                                 translateWrap((SoSceneTextureCubeMap::Wrap)PUBLIC(this)->wrapS.getValue()),
534                                 translateWrap((SoSceneTextureCubeMap::Wrap)PUBLIC(this)->wrapT.getValue()),
535                                 quality);
536     }
537   }
538 
539   if (!this->canrendertotexture && !this->pbuffervalid) {
540     assert(this->glimage);
541     assert(this->offscreenbuffer);
542     int cubeSideSize = this->glcontextsize[0] * this->glcontextsize[1] * 4;
543     unsigned char * cubeSidePtr = this->offscreenbuffer;
544 
545     // FIXME: what about  wrapS, wrapT, wrapR, and quality? - martin 20050427
546     for (int i=0; i<6; i++) {
547       this->glimage->setCubeMapImage((SoGLCubeMapImage::Target)i,
548                                      cubeSidePtr,
549                                      this->glcontextsize, 4);
550       cubeSidePtr += cubeSideSize;
551     }
552   }
553   this->glimagevalid = TRUE;
554   this->pbuffervalid = TRUE;
555 }
556 
557 void
destroyCamera(void)558 SoSceneTextureCubeMapP::destroyCamera(void)
559 {
560   if (this->cachedCamera) {
561     this->cachedCamera->unref();
562     this->cachedCamera = NULL;
563   }
564   if (this->cachedScene) {
565     this->cachedScene->unref();
566     this->cachedScene = NULL;
567   }
568 }
569 
570 SoCamera *
findCamera(void)571 SoSceneTextureCubeMapP::findCamera(void)
572 {
573   SoSearchAction sa;
574 
575   sa.setType(SoCamera::getClassTypeId());
576   sa.setInterest(SoSearchAction::FIRST);
577   sa.apply(PUBLIC(this)->scene.getValue());
578 
579   SoPath * path = sa.getPath();
580 
581   if (path == NULL)
582     return NULL;
583   else
584     return (SoCamera *)path->getTail();
585 }
586 
587 
588 SoCamera *
ensureCamera(void)589 SoSceneTextureCubeMapP::ensureCamera(void)
590 {
591   if (this->hasSceneChanged == FALSE) return this->cachedCamera;
592 
593   this->hasSceneChanged = FALSE;
594   SoCamera * camera    = this->findCamera();
595   SbBool     hasCamera = (camera != NULL); // does the scene provide a camera?
596 
597   if (hasCamera) {
598     if (this->cachedCamera != camera) {
599       if (this->cachedCamera) this->cachedCamera->unref();
600       this->cachedCamera = camera;
601       this->cachedCamera->ref();
602     }
603   }
604   else if (this->hadSceneCamera || this->cachedCamera == NULL) {
605     // create default camera:
606     static int didwarn = 0;
607     if (!didwarn) {
608       SoDebugError::postWarning("SoSceneTextureCubeMap::ensureCamera",
609                                 "The scene does not provide a camera. "
610                                 "A perspective camera at position (0,0,0) "
611                                 "will be used.");
612       didwarn = 1;
613     }
614     if (this->cachedCamera) this->cachedCamera->unref();
615     this->cachedCamera = new SoPerspectiveCamera;
616     this->cachedCamera->position = SbVec3f(0, 0, 0);
617     this->cachedCamera->nearDistance = 0.1f;
618     this->cachedCamera->farDistance = 100;
619     ((SoPerspectiveCamera*)this->cachedCamera)->heightAngle =
620       (float) (M_PI / 2.0f);
621     this->cachedCamera->ref();
622   }
623   assert(this->cachedCamera);
624 
625   SoNode * scene = PUBLIC(this)->scene.getValue();
626   if (hasCamera) {
627     if (scene != this->cachedScene) {
628       if (this->cachedScene) this->cachedScene->unref();
629       this->cachedScene = scene;
630       this->cachedScene->ref();
631     }
632   }
633   else if (this->cachedScene == NULL || this->hadSceneCamera) {
634     if (this->cachedScene) this->cachedScene->unref();
635     SoSeparator * root = new SoSeparator();
636     root->addChild(this->cachedCamera);
637     root->addChild(scene);
638     this->cachedScene = (SoNode *)root;
639     this->cachedScene->ref();
640   }
641   else {
642     assert(this->cachedScene->isOfType(SoSeparator::getClassTypeId()));
643     SoSeparator * root = (SoSeparator*)this->cachedScene;
644     assert(root->getNumChildren() == 2);
645     if (root->getChild(1) != scene)
646       ((SoSeparator *)this->cachedScene)->replaceChild(1, scene);
647   }
648 
649   this->hadSceneCamera = hasCamera;
650   return this->cachedCamera;
651 }
652 
653 SoNode *
updateCamera(const SoGLCubeMapImage::Target target)654 SoSceneTextureCubeMapP::updateCamera(const SoGLCubeMapImage::Target target)
655 {
656   SoCamera * camera = this->ensureCamera();
657 
658   switch (target) {
659   default:
660   case SoGLCubeMapImage::NEGATIVE_X:
661     camera->orientation.setValue(ROT_NEG_X);
662     break;
663   case SoGLCubeMapImage::POSITIVE_X:
664     camera->orientation.setValue(ROT_POS_X);
665     break;
666 
667   case SoGLCubeMapImage::NEGATIVE_Y:
668     camera->orientation.setValue(ROT_NEG_Y);
669     break;
670   case SoGLCubeMapImage::POSITIVE_Y:
671     camera->orientation.setValue(ROT_POS_Y);
672     break;
673 
674   case SoGLCubeMapImage::NEGATIVE_Z:
675     camera->orientation.setValue(ROT_NEG_Z);
676     break;
677   case SoGLCubeMapImage::POSITIVE_Z:
678     camera->orientation.setValue(ROT_POS_Z);
679     break;
680   }
681 
682   return this->cachedScene;
683 }
684 
685 void
prerendercb(void * userdata,SoGLRenderAction * COIN_UNUSED_ARG (action))686 SoSceneTextureCubeMapP::prerendercb(void * userdata, SoGLRenderAction * COIN_UNUSED_ARG(action))
687 {
688   SoSceneTextureCubeMap * thisp = (SoSceneTextureCubeMap*) userdata;
689   SbColor col = thisp->backgroundColor.getValue();
690   glClearColor(col[0], col[1], col[2], 1.0f);
691   glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
692 }
693 
694 #undef LOCK_GLIMAGE
695 #undef UNLOCK_GLIMAGE
696 #undef PRIVATE
697 #undef PUBLIC
698