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#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif // HAVE_CONFIG_H
38
39#include <Inventor/SbLinear.h>
40#include <Inventor/errors/SoDebugError.h>
41#include <Inventor/bundles/SoMaterialBundle.h>
42#include <Inventor/actions/SoGLRenderAction.h>
43#include <Inventor/actions/SoRayPickAction.h>
44#include <Inventor/actions/SoGetMatrixAction.h>
45#include <Inventor/actions/SoGetBoundingBoxAction.h>
46#include <Inventor/actions/SoHandleEventAction.h>
47#include <Inventor/elements/SoModelMatrixElement.h>
48#include <Inventor/elements/SoViewVolumeElement.h>
49#include <Inventor/misc/SoState.h>
50#include <Inventor/misc/SoChildList.h>
51
52#include <Inventor/@Gui@/common/gl.h>
53
54#include <Inventor/@Gui@/nodes/SoGuiPane.h>
55
56#define ZSCALE (1.0f)
57
58// *************************************************************************
59
60// FIXME: can't cache model matrix like this - use in multiple scene
61// graphs doesn't work then...
62
63class Pane {
64public:
65  SbMatrix modelmatrix;
66  SbVec3f position;
67  SbVec2f raypickpos;
68
69};
70
71// *************************************************************************
72
73#define PRIVATE(obj) ((Pane *) obj->internals)
74
75SO_NODE_SOURCE(SoGuiPane);
76
77void
78SoGuiPane::initClass(void)
79{
80  SO_NODE_INIT_CLASS(SoGuiPane, SoSeparator, "Separator");
81}
82
83SoGuiPane::SoGuiPane(void)
84{
85  this->internals = (void *) new Pane;
86  SO_NODE_CONSTRUCTOR(SoGuiPane);
87  SO_NODE_ADD_FIELD(worldSize, (SbVec3f(1.0f, 1.0f, 0.0f)));
88  SO_NODE_ADD_FIELD(objectSize, (SbVec3f(100.0f, 100.0f, 0.0f)));
89}
90
91SoGuiPane::~SoGuiPane(void)
92{
93  Pane * obj = PRIVATE(this);
94  delete obj;
95  this->internals = NULL;
96}
97
98void
99SoGuiPane::doAction(SoAction * action)
100{
101  SoState * state = action->getState();
102
103  state->push();
104
105  if ( state->isElementEnabled(SoModelMatrixElement::getClassStackIndex()) ) {
106    SbVec3f world = this->worldSize.getValue();
107    SbVec3f object = this->objectSize.getValue();
108    SbVec3f scalefactor(world[0] / object[0], world[1] / object[1], ZSCALE);
109    SoModelMatrixElement::scaleBy(state, this, scalefactor);
110    PRIVATE(this)->modelmatrix = SoModelMatrixElement::get(state);
111  }
112
113  SbBool backface = FALSE;
114  if ( state->isElementEnabled(SoViewVolumeElement::getClassStackIndex()) ) {
115    SbVec3f normal(0.0f, 0.0f, 1.0f);
116    PRIVATE(this)->modelmatrix.multDirMatrix(normal, normal);
117    normal.normalize();
118    SbVec3f campos = SoViewVolumeElement::get(state).getProjectionPoint();
119    SbVec3f center = (SbVec3f(0.0f, 0.0f, 0.0f) + this->objectSize.getValue()) / 2.0f;
120    PRIVATE(this)->modelmatrix.multVecMatrix(center, center);
121    SbVec3f camdir = center - campos;
122    camdir.normalize();
123    backface = (normal.dot(camdir) >= 0.0f) ? TRUE : FALSE;
124  }
125
126  if ( action->isOfType(SoHandleEventAction::getClassTypeId()) ) {
127    if ( backface ) {
128      // we don't react to events when facing away
129      goto culled;
130    }
131  }
132
133  if ( action->isOfType(SoRayPickAction::getClassTypeId()) ) {
134    SoRayPickAction * rpaction = (SoRayPickAction *) action;
135    rpaction->setObjectSpace();
136
137    SbPlane plane(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f);
138    SbLine line(rpaction->getLine());
139    // find intersection with SbPlane
140    SbVec3f point;
141    PRIVATE(this)->raypickpos.setValue(-1.0f, -1.0f);
142    if ( plane.intersect(line, point) ) {
143      SbVec3f object = this->objectSize.getValue();
144      SbVec2f pos(point[0], point[1]);
145      if ( pos[0] < 0.0f || pos[1] < 0.0f || pos[0] > object[0] || pos[1] > object[1] ) {
146        goto culled;
147      } else {
148        PRIVATE(this)->raypickpos = pos;
149      }
150    }
151    else goto culled;
152  }
153  inherited::doAction(action);
154culled:
155  state->pop();
156}
157
158void
159SoGuiPane::GLRenderBelowPath(SoGLRenderAction * action)
160{
161  SoState * state = action->getState();
162  state->push();
163
164  SbVec3f world = this->worldSize.getValue();
165  SbVec3f object = this->objectSize.getValue();
166
167  // adjust scale for new object space
168  SbVec3f scalefactor(world[0] / object[0], world[1] / object[1], ZSCALE);
169  SoModelMatrixElement::scaleBy(state, this, scalefactor);
170
171  // store current model matrix
172  PRIVATE(this)->modelmatrix = SoModelMatrixElement::get(state);
173  PRIVATE(this)->position = SbVec3f(0.0f, 0.0f, 0.0f);
174
175
176  SbVec3f normal(0.0f, 0.0f, 1.0f);
177  PRIVATE(this)->modelmatrix.multDirMatrix(normal, normal);
178  normal.normalize();
179
180  SbVec3f campos = SoViewVolumeElement::get(state).getProjectionPoint();
181  SbVec3f center = (SbVec3f(0.0f, 0.0f, 0.0f) + this->objectSize.getValue()) / 2.0f;
182  PRIVATE(this)->modelmatrix.multVecMatrix(center, center);
183
184  SbVec3f camdir = SoViewVolumeElement::get(state).getProjectionDirection();
185  camdir = center - campos;
186  camdir.normalize();
187
188  const float dot = normal.dot(camdir);
189
190  const SbBool backface = (dot >= 0.0f) ? TRUE : FALSE;
191
192  SoMaterialBundle mb(action);
193  mb.sendFirst();
194
195  if ( object[0] != 0.0f && object[1] != 0.0f ) { // draw pane
196    if ( backface ) glNormal3f(0.0f, 0.0f, -1.0f);
197    else glNormal3f(0.0f, 0.0f, 1.0f);
198    glBegin(GL_QUADS);
199    glVertex3f(0.0f, 0.0f, 0.0f);
200    glVertex3f(0.0f, object[1], 0.0f);
201    glVertex3f(object[0], object[1], 0.0f);
202    glVertex3f(object[0], 0.0f, 0.0f);
203    glEnd(); // GL_QUADS
204  }
205
206  if ( backface ) goto finish;
207
208  glDisable(GL_DEPTH_TEST);
209  glDepthMask(FALSE);
210
211  // TODO: it would probably be a good idea to set up clipping around the pane
212  inherited::GLRenderBelowPath(action);
213
214  glDepthMask(TRUE);
215  glEnable(GL_DEPTH_TEST);
216
217finish:
218  state->pop();
219}
220
221void
222SoGuiPane::GLRenderInPath(SoGLRenderAction * action)
223{
224  SoDebugError::postInfo("SoGuiPane::GLRenderInPath", "implementation is out of date");
225  SoState * state = action->getState();
226  state->push();
227
228  SbVec3f world = this->worldSize.getValue();
229  SbVec3f object = this->objectSize.getValue();
230
231  SoMaterialBundle mb(action);
232  mb.sendFirst();
233
234  // draw pane
235  glBegin(GL_QUADS);
236  glVertex3f(0.0f, 0.0f, 0.0f);
237  glVertex3f(0.0f, world[1], 0.0f);
238  glVertex3f(world[0], world[1], 0.0f);
239  glVertex3f(world[0], 0.0f, 0.0f);
240  glEnd(); // GL_QUADS
241
242  // adjust scale for new object space
243  SbVec3f scalefactor(world[0] / object[0], world[1] / object[1], ZSCALE);
244  SoModelMatrixElement::scaleBy(state, this, scalefactor);
245
246  // store current model matrix
247  PRIVATE(this)->modelmatrix = SoModelMatrixElement::get(state);
248  PRIVATE(this)->position = SbVec3f(0.0f, 0.0f, 0.0f);
249
250  GLint olddepthfunc = 0;
251  glGetIntegerv(GL_DEPTH_FUNC, &olddepthfunc);
252  glDepthFunc(GL_LEQUAL);
253
254  // FIXME: set up clipping around pane
255  inherited::GLRenderInPath(action);
256
257  glDepthFunc((GLenum) olddepthfunc);
258  state->pop();
259}
260
261void
262SoGuiPane::GLRenderOffPath(SoGLRenderAction * action)
263{
264  // is this one ever invoked?
265  SoDebugError::postInfo("SoGuiPane::GLRenderOffPath", "no implementation yet");
266  inherited::GLRenderOffPath(action);
267}
268
269void
270SoGuiPane::pick(SoPickAction * action)
271{
272  this->doAction(action);
273}
274
275void
276SoGuiPane::rayPick(SoRayPickAction * action)
277{
278  this->doAction(action);
279}
280
281void
282SoGuiPane::getMatrix(SoGetMatrixAction * action)
283{
284  SoDebugError::postInfo("SoGuiPane::getMatrix", "invoked");
285  SbVec3f world = this->worldSize.getValue();
286  SbVec3f object = this->objectSize.getValue();
287  SbVec3f scalefactor(world[0] / object[0], world[1] / object[1], ZSCALE);
288  SbMatrix matrix = SbMatrix::identity();
289  matrix.setScale(scalefactor);
290  action->getMatrix().multLeft(matrix);
291  // FIXME: update inverse matrix
292  inherited::getMatrix(action);
293}
294
295void
296SoGuiPane::handleEvent(SoHandleEventAction * action)
297{
298  this->doAction(action);
299}
300
301void
302SoGuiPane::getBoundingBox(SoGetBoundingBoxAction * action)
303{
304  SbVec3f min = SbVec3f(0.0f, 0.0f, 0.0f);
305  SbVec3f max = this->worldSize.getValue();
306  min[2] = 0.0f; // -0.001f;
307  max[2] = 0.0f; // 0.001f;
308  SbBox3f box(min, max);
309  action->extendBy(box);
310}
311
312SbVec2f
313SoGuiPane::getRayPickIntersectionPoint(void) const
314{
315  return PRIVATE(this)->raypickpos;
316}
317
318void
319SoGuiPane::moveTo(SoState * state, SbVec3f position)
320{
321  SbVec3f object = this->objectSize.getValue();
322  if ( position[0] < 0.0f ) position[0] = object[0] + position[0];
323  if ( position[1] < 0.0f ) position[1] = object[1] + position[1];
324  PRIVATE(this)->position = position;
325  SbVec3f translation(0.0f, 0.0f, 0.0f);
326  translation[0] = PRIVATE(this)->position[0];
327  translation[1] = PRIVATE(this)->position[1];
328  SoModelMatrixElement::set(state, this, PRIVATE(this)->modelmatrix);
329  // SoDebugError::postInfo("SoGuiPane::moveTo", "origo in %g %g", translation[0], translation[1]);
330  SoModelMatrixElement::translateBy(state, this, translation);
331}
332
333void
334SoGuiPane::applyMoveTo(SoGetMatrixAction * action, SbVec3f position)
335{
336  // FIXME: support negative coordinates
337  SoDebugError::postInfo("SoGuiPane::applyMoveTo", "target %g %g", position[0], position[1]);
338  action->getMatrix() = PRIVATE(this)->modelmatrix;
339  SbMatrix matrix = SbMatrix::identity();
340  matrix.setTranslate(position);
341  action->getMatrix().multLeft(matrix);
342  // FIXME: update inverse matrix
343}
344
345void
346SoGuiPane::moveBy(SoState * state, SbVec3f offset)
347{
348  PRIVATE(this)->position += offset;
349  SbVec3f translation(0.0f, 0.0f, 0.0f);
350  translation[0] = PRIVATE(this)->position[0];
351  translation[1] = PRIVATE(this)->position[1];
352  SoModelMatrixElement::set(state, this, PRIVATE(this)->modelmatrix);
353  // SoDebugError::postInfo("SoGuiPane::moveBy", "origo in %g %g", translation[0], translation[1]);
354  SoModelMatrixElement::translateBy(state, this, translation);
355}
356
357void
358SoGuiPane::applyMoveBy(SoGetMatrixAction * action, SbVec3f offset)
359{
360  SoDebugError::postInfo("SoGuiPane::applyMoveBy", "offset %g %g", offset[0], offset[1]);
361  action->getMatrix() = PRIVATE(this)->modelmatrix;
362  SbMatrix matrix = SbMatrix::identity();
363  SbVec3f position = PRIVATE(this)->position + offset;
364  matrix.setTranslate(position);
365  action->getMatrix().multLeft(matrix);
366  // FIXME: update inverse matrix
367}
368
369#undef ZSCALE
370#undef PRIVATE
371
372