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