1// @configure_input@
2
3/**************************************************************************\
4 * Copyright (c) Kongsberg Oil & Gas Technologies AS
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33\**************************************************************************/
34
35/*
36 * TODO
37 * - field "SoSFFloat delay" for making node wait a certain time after
38 *   last scene change before re-rendering the texture
39 * - field "SoSFBool dirty" for making texture show itself as dirty
40 *   (stippled?) until it has been rerendered (in case it takes time to
41 *   rerender scene).
42 */
43
44#include <assert.h>
45#include <string.h>
46
47#include <Inventor/errors/SoDebugError.h>
48#include <Inventor/sensors/SoFieldSensor.h>
49#include <Inventor/sensors/SoOneShotSensor.h>
50#include <Inventor/actions/SoGLRenderAction.h>
51#include <Inventor/SoOffscreenRenderer.h>
52#include <Inventor/misc/SoNotification.h>
53
54#include <Inventor/@Gui@/nodes/SoGuiSceneTexture2.h>
55
56// *************************************************************************
57
58class SceneTexture2 {
59public:
60  SceneTexture2(void);
61
62  SoGuiSceneTexture2 * api;
63
64  SbVec2s prevsize;
65  SoOffscreenRenderer * renderer;
66
67  SoFieldSensor * size_sensor;
68  static void size_updated_cb(void * closure, SoSensor * sensor);
69
70  SoOneShotSensor * render_sensor;
71  static void render_cb(void * closure, SoSensor * sensor);
72};
73
74// *************************************************************************
75
76#define PRIVATE(obj) ((SceneTexture2 *) obj->internals)
77
78void
79SoGuiSceneTexture2::initClass(void)
80{
81  SO_NODE_INIT_CLASS(SoGuiSceneTexture2, SoTexture2, "Texture2");
82}
83
84SO_NODE_SOURCE(SoGuiSceneTexture2);
85
86SoGuiSceneTexture2::SoGuiSceneTexture2(void)
87{
88  this->internals = new SceneTexture2;
89  PRIVATE(this)->api = this;
90
91  SO_NODE_CONSTRUCTOR(SoGuiSceneTexture2);
92  SO_NODE_ADD_FIELD(size, (SbVec2f(256.0f, 256.0f)));
93  SO_NODE_ADD_FIELD(scene, (NULL));
94
95  PRIVATE(this)->size_sensor = new SoFieldSensor(SceneTexture2::size_updated_cb, PRIVATE(this));
96  PRIVATE(this)->size_sensor->attach(&(this->size));
97  PRIVATE(this)->render_sensor = new SoOneShotSensor(SceneTexture2::render_cb, PRIVATE(this));
98}
99
100SoGuiSceneTexture2::~SoGuiSceneTexture2(void)
101{
102  delete PRIVATE(this)->size_sensor;
103  delete PRIVATE(this)->render_sensor;
104  if ( PRIVATE(this)->renderer != NULL ) {
105    delete PRIVATE(this)->renderer;
106  }
107}
108
109// We overload notify() because we need to know when the scene graph has
110// been updated, but want to stop that notification from propagating
111// through the external scene graph.  We only want the texture update to
112// trigger re-rendering.
113void
114SoGuiSceneTexture2::notify(SoNotList * list)
115{
116  if ( list->getLastField() == &(this->scene) ) {
117    PRIVATE(this)->render_sensor->schedule();
118    return;
119  }
120  inherited::notify(list);
121}
122
123//
124// we don't want to write out the image and filename fields.
125//
126void
127SoGuiSceneTexture2::write(SoWriteAction * action)
128{
129  this->image.setDefault(TRUE);
130  this->filename.setDefault(TRUE);
131  inherited::write(action);
132}
133
134
135#undef PRIVATE
136
137// *************************************************************************
138
139#define PUBLIC(obj) (((SceneTexture2 *) obj)->api)
140
141SceneTexture2::SceneTexture2(void)
142{
143  this->api = NULL;
144  this->prevsize = SbVec2s(-1, -1);
145  this->renderer = NULL;
146  this->size_sensor = NULL;
147  this->render_sensor = NULL;
148}
149
150void
151SceneTexture2::size_updated_cb(void * closure, SoSensor * sensor)
152{
153  assert(closure);
154  SceneTexture2 * me = (SceneTexture2 *) closure;
155  SbVec2f tempsize = PUBLIC(me)->size.getValue();
156  SbVec2s size;
157  size[0] = (short) tempsize[0];
158  size[1] = (short) tempsize[1];
159  if ( size != me->prevsize ) {
160    if ( me->renderer != NULL ) {
161      me->renderer->setViewportRegion(SbViewportRegion(size));
162      PUBLIC(me)->image.setValue(size, 3, NULL);
163      me->render_sensor->schedule();
164    }
165    me->prevsize = size;
166  }
167}
168
169void
170SceneTexture2::render_cb(void * closure, SoSensor * sensor)
171{
172  assert(closure);
173  SceneTexture2 * me = (SceneTexture2 *) closure;
174  SbVec2f tempsize = PUBLIC(me)->size.getValue();
175  SbVec2s size;
176  size[0] = (short) tempsize[0];
177  size[1] = (short) tempsize[1];
178  int nc = 3;
179  SoNode * scene = PUBLIC(me)->scene.getValue();
180  SbBool save = PUBLIC(me)->image.enableNotify(FALSE);
181  if ( scene != NULL ) {
182    if ( me->renderer == NULL ) {
183      me->renderer = new SoOffscreenRenderer(SbViewportRegion(size));
184      me->renderer->setComponents(SoOffscreenRenderer::RGB);
185      me->renderer->getGLRenderAction()->setTransparencyType(SoGLRenderAction::BLEND);
186      me->prevsize = size;
187      PUBLIC(me)->image.setValue(size, nc, NULL);
188    }
189    me->renderer->render(scene);
190    unsigned char * renderbuffer = me->renderer->getBuffer();
191    unsigned char * imagebytes = PUBLIC(me)->image.startEditing(size, nc);
192    memcpy(imagebytes, renderbuffer, size[0] * size[1] * nc);
193    PUBLIC(me)->image.finishEditing();
194  } else {
195    unsigned char * imagebytes = PUBLIC(me)->image.startEditing(size, nc);
196    memset(imagebytes, 0, size[0] * size[1] * nc);
197    PUBLIC(me)->image.finishEditing();
198  }
199  PUBLIC(me)->image.enableNotify(save);
200  if ( save ) PUBLIC(me)->image.touch();
201}
202
203#undef PUBLIC
204
205// *************************************************************************
206