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