~ubuntu-branches/ubuntu/oneiric/soqt/oneiric

« back to all changes in this revision

Viewing changes to src/Inventor/Qt/common/viewers/SoGuiViewer.cpp.in

  • Committer: Bazaar Package Importer
  • Author(s): Steve M. Robbins
  • Date: 2004-05-29 02:58:50 UTC
  • Revision ID: james.westby@ubuntu.com-20040529025850-phd20eva5uyhhdrf
Tags: upstream-1.0.2
ImportĀ upstreamĀ versionĀ 1.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**************************************************************************\
 
2
 *
 
3
 *  This file is part of the Coin 3D visualization library.
 
4
 *  Copyright (C) 1998-2003 by Systems in Motion.  All rights reserved.
 
5
 *
 
6
 *  This library is free software; you can redistribute it and/or
 
7
 *  modify it under the terms of the GNU General Public License
 
8
 *  ("GPL") version 2 as published by the Free Software Foundation.
 
9
 *  See the file LICENSE.GPL at the root directory of this source
 
10
 *  distribution for additional information about the GNU GPL.
 
11
 *
 
12
 *  For using Coin with software that can not be combined with the GNU
 
13
 *  GPL, and for taking advantage of the additional benefits of our
 
14
 *  support services, please contact Systems in Motion about acquiring
 
15
 *  a Coin Professional Edition License.
 
16
 *
 
17
 *  See <URL:http://www.coin3d.org> for more information.
 
18
 *
 
19
 *  Systems in Motion, Teknobyen, Abels Gate 5, 7030 Trondheim, NORWAY.
 
20
 *  <URL:http://www.sim.no>.
 
21
 *
 
22
\**************************************************************************/
 
23
 
 
24
// @configure_input@
 
25
 
 
26
// NOTE: The So@Gui@Viewer.cpp sourcecode file is completely
 
27
// autogenerated from "templatized" source code.
 
28
 
 
29
// *************************************************************************
 
30
 
 
31
/*!
 
32
  \class So@Gui@Viewer Inventor/@Gui@/viewers/So@Gui@Viewer.h
 
33
  \brief The So@Gui@Viewer class is the top level base viewer class.
 
34
  \ingroup components viewers
 
35
 
 
36
  This is an abstract class, which adds the following features to it's
 
37
  So@Gui@RenderArea superclass: convenient methods for camera
 
38
  handling, an automatic headlight configuration.
 
39
 
 
40
  As for the camera handling: when setting a new scenegraph for the
 
41
  viewer, the scenegraph will automatically be scanned for a node
 
42
  derived from SoCamera. If not found, the viewer will itself set up a
 
43
  camera for the scene. The camera can then be conveniently controlled
 
44
  by the application programmers in many aspects:
 
45
 
 
46
  <ul>
 
47
 
 
48
  <li>camera type: toggle between using an orthographic camera and a
 
49
  perspective camera with So@Gui@Viewer::toggleCameraType()</li>
 
50
 
 
51
  <li>zoom out to exactly encompass all scene geometry within the view
 
52
  by using So@Gui@Viewer::viewAll()</li>
 
53
 
 
54
  <li>tag a specific position and orientation for the camera as the
 
55
  "home" position with So@Gui@Viewer::saveHomePosition(), which one
 
56
  can then return to by using
 
57
  So@Gui@Viewer::resetToHomePosition()</li>
 
58
 
 
59
  <li>automatically fit the near and far clipping planes of the camera
 
60
  around the scene's geometry by using
 
61
  So@Gui@Viewer::setAutoClipping()</li>
 
62
 
 
63
  <li>control stereo viewing parameters</li>
 
64
 
 
65
  </ul>
 
66
 
 
67
 
 
68
  The So@Gui@Viewer class automatically adds a headlight to the scene,
 
69
  which will always point in the approximate same direction as the
 
70
  current viewer camera, thereby securing that the scene geometry is
 
71
  always lighted and visible. (If you don't want the constant
 
72
  headlight, but rather want to light the scene on your own, this
 
73
  behavior can be turned off with So@Gui@Viewer::setHeadlight()).
 
74
 
 
75
 
 
76
  So@Gui@Viewer-derived viewers all inherit the following keyboard
 
77
  controls from this class (but only when the viewer is in "examine
 
78
  mode", ie So@Gui@Viewer::isViewing() returns \c TRUE):
 
79
 
 
80
  <ul>
 
81
 
 
82
  <li>"s": put the viewer in "seek mode", where the end user may click
 
83
  anywhere on scene geometry to trigger an animation which moves the
 
84
  camera towards the point clicked</li>
 
85
 
 
86
  <li>"Home": hit this key to move camera back to last saved "home
 
87
  position"</li>
 
88
 
 
89
  <li>arrow keys: moves camera slightly left, right, up or down</li>
 
90
 
 
91
  <li>"q": exit application</li>
 
92
 
 
93
  </ul>
 
94
*/
 
95
 
 
96
// *************************************************************************
 
97
 
 
98
 
 
99
/*!
 
100
  \enum So@Gui@Viewer::AutoClippingStrategy
 
101
  Enum for auto clipping strategy.
 
102
  
 
103
  \sa setAutoClippingStrategy()
 
104
*/
 
105
 
 
106
/*!
 
107
  \var So@Gui@Viewer::AutoClippingStrategy So@Gui@Viewer::CONSTANT_NEAR_PLANE
 
108
 
 
109
  Constant near plane auto clipping strategy. Explained in detail in
 
110
  the documentation for the So@Gui@Viewer::setAutoClippingStrategy()
 
111
  method.
 
112
*/
 
113
 
 
114
/*!
 
115
  \var So@Gui@Viewer::AutoClippingStrategy So@Gui@Viewer::VARIABLE_NEAR_PLANE
 
116
 
 
117
  Variable near plane auto clipping strategy. Explained in detail in
 
118
  the documentation for the So@Gui@Viewer::setAutoClippingStrategy()
 
119
  method.
 
120
*/
 
121
 
 
122
#if HAVE_CONFIG_H
 
123
#include <config.h>
 
124
#endif // HAVE_CONFIG_H
 
125
 
 
126
#include <stdlib.h>
 
127
#include <string.h>
 
128
#include <math.h>
 
129
 
 
130
#include <Inventor/@Gui@/common/gl.h>
 
131
#include <Inventor/@Gui@/SoAny.h>
 
132
#include <Inventor/@Gui@/common/SbGuiList.h>
 
133
 
 
134
#include <Inventor/SoDB.h>
 
135
 
 
136
#include <Inventor/nodes/SoBaseColor.h>
 
137
#include <Inventor/nodes/SoComplexity.h>
 
138
#include <Inventor/nodes/SoDirectionalLight.h>
 
139
#include <Inventor/nodes/SoDrawStyle.h>
 
140
#include <Inventor/nodes/SoLightModel.h>
 
141
#include <Inventor/nodes/SoLocateHighlight.h>
 
142
#include <Inventor/nodes/SoMaterialBinding.h>
 
143
#include <Inventor/nodes/SoOrthographicCamera.h>
 
144
#include <Inventor/nodes/SoPerspectiveCamera.h>
 
145
#include <Inventor/nodes/SoSeparator.h>
 
146
#include <Inventor/nodes/SoSwitch.h>
 
147
#include <Inventor/nodekits/SoBaseKit.h>
 
148
 
 
149
#include <Inventor/actions/SoGetBoundingBoxAction.h>
 
150
#include <Inventor/actions/SoGetMatrixAction.h>
 
151
#include <Inventor/actions/SoSearchAction.h>
 
152
#include <Inventor/actions/SoRayPickAction.h>
 
153
 
 
154
#include <Inventor/events/SoMouseButtonEvent.h>
 
155
#include <Inventor/errors/SoDebugError.h>
 
156
#include <Inventor/misc/SoCallbackList.h>
 
157
#include <Inventor/sensors/SoTimerSensor.h>
 
158
#include <Inventor/events/SoKeyboardEvent.h>
 
159
#include <Inventor/SoSceneManager.h>
 
160
#include <Inventor/SoPickedPoint.h>
 
161
#include <Inventor/SoLists.h>
 
162
#include <Inventor/SbLinear.h>
 
163
 
 
164
#include <so@gui@defs.h>
 
165
#include <Inventor/@Gui@/So@Gui@.h>
 
166
#include <Inventor/@Gui@/viewers/So@Gui@Viewer.h>
 
167
#include <math.h>
 
168
#include <float.h> // FLT_MAX
 
169
 
 
170
#if HAVE_SOPOLYGONOFFSET
 
171
#include <Inventor/nodes/SoPolygonOffset.h>
 
172
#endif // HAVE_SOPOLYGONOFFSET
 
173
 
 
174
// (note: this *must* be a #define, not a static variable -- to avoid
 
175
// initialization race conditions with the static variables being set
 
176
// to the value of this)
 
177
#define UNINITIALIZED_ENVVAR -1 // value of envvars before tested
 
178
 
 
179
// environment variables
 
180
static int COIN_SHOW_FPS_COUNTER = UNINITIALIZED_ENVVAR;
 
181
 
 
182
// *************************************************************************
 
183
 
 
184
#ifndef DOXYGEN_SKIP_THIS
 
185
 
 
186
// The private data for the So@Gui@Viewer.
 
187
class So@Gui@ViewerP {
 
188
public:
 
189
  So@Gui@ViewerP(So@Gui@Viewer * publ);
 
190
  ~So@Gui@ViewerP(void);
 
191
 
 
192
  SoSeparator * createSuperScene(void);
 
193
  SoSeparator * createFPSSuperimposition(void);
 
194
 
 
195
  static void convertOrtho2Perspective(const SoOrthographicCamera * in,
 
196
                                       SoPerspectiveCamera * out);
 
197
  static void convertPerspective2Ortho(const SoPerspectiveCamera * in,
 
198
                                       SoOrthographicCamera * out);
 
199
 
 
200
  SoCamera * camera;
 
201
  So@Gui@Viewer::Type type;
 
202
  SbBool viewingflag;
 
203
  SoGetBoundingBoxAction * autoclipbboxaction;
 
204
  SoSeparator * sceneroot;
 
205
  SoNode * scenegraph;
 
206
 
 
207
 
 
208
  // Seek functionality
 
209
  SoTimerSensor * seeksensor;
 
210
  float seekperiod;
 
211
  SbBool inseekmode;
 
212
  SbBool seektopoint;
 
213
  SbVec3f camerastartposition, cameraendposition;
 
214
  SbRotation camerastartorient, cameraendorient;
 
215
  float seekdistance;
 
216
  SbBool seekdistanceabs;
 
217
 
 
218
  // Camera handling
 
219
  SbBool deletecamera;
 
220
 
 
221
  // Home position storage.
 
222
  SoOrthographicCamera * storedortho;
 
223
  SoPerspectiveCamera * storedperspective;
 
224
 
 
225
  SoDirectionalLight * headlight;
 
226
 
 
227
  // Drawstyles
 
228
  So@Gui@Viewer::DrawStyle drawstyles[2];
 
229
  SoSwitch * drawstyleroot, * hiddenlineroot, * polygonoffsetparent;
 
230
  SoBaseColor * sobasecolor;
 
231
  SoComplexity * socomplexity;
 
232
  SoDrawStyle * sodrawstyle;
 
233
  SoLightModel * solightmodel;
 
234
  SoMaterialBinding * somaterialbinding;
 
235
  SoSeparator * usersceneroot;
 
236
  SoSwitch * superimpositionroot;
 
237
#if HAVE_SOPOLYGONOFFSET
 
238
  SoPolygonOffset * sopolygonoffset;
 
239
#endif // HAVE_SOPOLYGONOFFSET
 
240
  // Automatic setting of clipping planes
 
241
  SbBool adjustclipplanes;
 
242
 
 
243
  SoSeparator * fpsRoot;
 
244
 
 
245
  // Keep track of the frames-per-second counter.
 
246
  // Const value trick for old compilers.
 
247
  enum Constants { FRAMESARRAY_SIZE = 100 };
 
248
  SbVec2f frames[FRAMESARRAY_SIZE];
 
249
  float totalcoin, totaldraw;
 
250
  double lastgettimeofday;
 
251
  int framecount;
 
252
 
 
253
  // Misc
 
254
  SoType cameratype;
 
255
  SbBool cursoron, localsetbuffertype;
 
256
  SoCallbackList * interactionstartCallbacks, * interactionendCallbacks;
 
257
  int interactionnesting;
 
258
  So@Gui@Viewer::BufferType buffertype;
 
259
  SbBool stereoviewing;
 
260
  float stereooffset;
 
261
  SbColor wireframeoverlaycolor;
 
262
 
 
263
  void reallyRedraw(const SbBool clearcol, const SbBool clearz = TRUE);
 
264
 
 
265
  // Seek functionality
 
266
  static void seeksensorCB(void * data, SoSensor *);
 
267
 
 
268
  // Drawstyles
 
269
  void changeDrawStyle(So@Gui@Viewer::DrawStyle style);
 
270
  SbBool drawInteractiveAsStill(void) const;
 
271
  SbBool drawAsHiddenLine(void) const;
 
272
  SbBool drawAsWireframeOverlay(void) const;
 
273
  So@Gui@Viewer::DrawStyle currentDrawStyle(void) const;
 
274
 
 
275
  // Automatic setting of clipping planes
 
276
  void setClippingPlanes(void);
 
277
 
 
278
  // Methods to keep track of frames-per-second value.
 
279
  void resetFrameCounter(void);
 
280
  SbVec2f addFrametime(const double ft);
 
281
  void recordFPS(const double rendertime);
 
282
 
 
283
  // Misc
 
284
  static void interactivestartCB(void *, So@Gui@Viewer * thisp);
 
285
  static void interactiveendCB(void *, So@Gui@Viewer * thisp);
 
286
  void moveCameraScreen(const SbVec2f & screenpos);
 
287
  void getCameraCoordinateSystem(SoCamera * camera, SoNode * root,
 
288
                                 SbMatrix & matrix, SbMatrix & inverse);
 
289
 
 
290
  SoSearchAction * searchaction;
 
291
  SoGetMatrixAction * matrixaction;
 
292
  SbPList * superimpositions;
 
293
  SbGuiList<SbBool> superimpositionsenabled;
 
294
  So@Gui@Viewer * pub;
 
295
 
 
296
  // auto clipping parameters
 
297
  So@Gui@Viewer::AutoClippingStrategy autoclipstrategy;
 
298
  float autoclipvalue;
 
299
  So@Gui@AutoClippingCB * autoclipcb;
 
300
  void * autoclipuserdata;
 
301
};
 
302
 
 
303
#define PRIVATE(ptr) (ptr->pimpl)
 
304
#define PUBLIC(ptr) (ptr->pub)
 
305
 
 
306
 
 
307
So@Gui@ViewerP::So@Gui@ViewerP(So@Gui@Viewer * publ)
 
308
{
 
309
  PUBLIC(this) = publ;
 
310
  this->searchaction = new SoSearchAction;
 
311
  this->matrixaction = new SoGetMatrixAction(SbViewportRegion(100,100));
 
312
  this->superimpositions = NULL;
 
313
 
 
314
  this->storedortho = new SoOrthographicCamera;
 
315
  this->storedortho->ref();
 
316
  this->storedperspective = new SoPerspectiveCamera;
 
317
  this->storedperspective->ref();
 
318
 
 
319
  // initialize auto clipping parameters
 
320
  this->autoclipstrategy = So@Gui@Viewer::VARIABLE_NEAR_PLANE;
 
321
  this->autoclipvalue = 0.6f;
 
322
  this->autoclipcb = NULL;
 
323
}
 
324
 
 
325
So@Gui@ViewerP::~So@Gui@ViewerP()
 
326
{
 
327
  if (this->superimpositions) {
 
328
    delete this->superimpositions;
 
329
    this->superimpositions = NULL;
 
330
  }
 
331
  delete this->searchaction;
 
332
  delete this->matrixaction;
 
333
 
 
334
  this->storedortho->unref();
 
335
  this->storedperspective->unref();
 
336
}
 
337
 
 
338
SoSeparator *
 
339
So@Gui@ViewerP::createSuperScene(void)
 
340
{
 
341
  static const char * superSceneGraph[] =
 
342
  {
 
343
    "#Inventor V2.1 ascii",
 
344
    "",
 
345
    "Separator {",
 
346
    "  renderCaching OFF",
 
347
    "  renderCulling OFF",
 
348
    "  pickCulling OFF",
 
349
    "  boundingBoxCaching OFF",
 
350
    
 
351
    // Headlight. By inserting this before any scenegraph camera, the
 
352
    // light will always be pointing in the correct direction.
 
353
    "  DEF so@gui@->headlight DirectionalLight {",
 
354
    "    direction 1 -1 -10",
 
355
    "  }",
 
356
 
 
357
    "  DEF so@gui@->drawstyleroot Switch {",
 
358
    "    whichChild -1",
 
359
    "    DEF so@gui@->lightmodel LightModel {",
 
360
    "      model BASE_COLOR",
 
361
    "    }",
 
362
    "    DEF so@gui@->drawstyle DrawStyle {",
 
363
    "      pointSize ~",
 
364
    "      lineWidth ~",
 
365
    "      linePattern ~",
 
366
    "    }",
 
367
    "    DEF so@gui@->complexity Complexity {",
 
368
    "      textureQuality 0.0",
 
369
    "      value 0.1",
 
370
    "    }",
 
371
    "  }",
 
372
    "  DEF so@gui@->hiddenlineroot Switch {",
 
373
    "    whichChild -1",
 
374
    "    DEF so@gui@->basecolor BaseColor { }",
 
375
    "    DEF so@gui@->materialbinding MaterialBinding {",
 
376
    "      value OVERALL",
 
377
    "    }",
 
378
    "    DEF so@gui@->polygonoffsetparent Switch {",
 
379
    "      whichChild -1",
 
380
#if HAVE_SOPOLYGONOFFSET
 
381
    "      DEF so@gui@->polygonoffset PolygonOffset { }",
 
382
#endif // HAVE_SOPOLYGONOFFSET
 
383
    "    }",
 
384
    "  }",
 
385
    "  DEF so@gui@->userscenegraphroot Separator {",
 
386
    "  }",
 
387
    "}",
 
388
    NULL
 
389
  };
 
390
 
 
391
  int i, bufsize;
 
392
  for (i = bufsize = 0; superSceneGraph[i]; i++)
 
393
    bufsize += strlen(superSceneGraph[i]) + 1;
 
394
  char * buf = new char [bufsize + 1];
 
395
  for (i = bufsize = 0; superSceneGraph[i]; i++) {
 
396
    strcpy(buf + bufsize, superSceneGraph[i]);
 
397
    bufsize += strlen(superSceneGraph[i]);
 
398
    buf[bufsize] = '\n';
 
399
    bufsize++;
 
400
  }
 
401
  SoInput * input = new SoInput;
 
402
  input->setBuffer(buf, bufsize);
 
403
  SoNode * root = NULL;
 
404
  SbBool ok = SoDB::read(input, root);
 
405
  delete input;
 
406
  delete [] buf;
 
407
  if (!ok) {
 
408
    SoDebugError::post("So@Gui@Viewer::So@Gui@Viewer",
 
409
                       "couldn't create viewer superscene");
 
410
    return NULL;
 
411
  }
 
412
  assert(root->isOfType(SoSeparator::getClassTypeId()));
 
413
  root->ref();
 
414
 
 
415
  this->searchaction->reset();
 
416
  this->searchaction->setSearchingAll(TRUE);
 
417
  this->searchaction->setInterest(SoSearchAction::FIRST);
 
418
 
 
419
#define LOCATE_NODE(member, type, name) \
 
420
  do { \
 
421
    member = NULL; \
 
422
    this->searchaction->setName(SbName(name)); \
 
423
    this->searchaction->apply(root); \
 
424
    if (this->searchaction->getPath() != NULL) { \
 
425
      SoNode * node = this->searchaction->getPath()->getTail(); \
 
426
      assert(node != NULL); \
 
427
      if (node->isOfType(type::getClassTypeId())) \
 
428
        member = (type *) node; \
 
429
    } else { \
 
430
      SoDebugError::post("So@Gui@ViewerP::createSuperScene", \
 
431
                         "didn't locate node \"%s\"", name); \
 
432
    } \
 
433
  } while (FALSE)
 
434
 
 
435
  LOCATE_NODE(this->headlight, SoDirectionalLight, "so@gui@->headlight");
 
436
  LOCATE_NODE(this->drawstyleroot, SoSwitch, "so@gui@->drawstyleroot");
 
437
  LOCATE_NODE(this->hiddenlineroot, SoSwitch, "so@gui@->hiddenlineroot");
 
438
  LOCATE_NODE(this->polygonoffsetparent, SoSwitch,
 
439
              "so@gui@->polygonoffsetparent");
 
440
  LOCATE_NODE(this->usersceneroot, SoSeparator, "so@gui@->userscenegraphroot");
 
441
 
 
442
  LOCATE_NODE(this->sobasecolor, SoBaseColor, "so@gui@->basecolor");
 
443
  LOCATE_NODE(this->socomplexity, SoComplexity, "so@gui@->complexity");
 
444
  LOCATE_NODE(this->sodrawstyle, SoDrawStyle, "so@gui@->drawstyle");
 
445
  LOCATE_NODE(this->solightmodel, SoLightModel, "so@gui@->lightmodel");
 
446
  LOCATE_NODE(this->somaterialbinding, SoMaterialBinding, "so@gui@->materialbinding");
 
447
  if (this->sobasecolor) this->sobasecolor->setOverride(TRUE);
 
448
  if (this->socomplexity) this->socomplexity->setOverride(TRUE);
 
449
  if (this->sodrawstyle) this->sodrawstyle->setOverride(TRUE);
 
450
  if (this->solightmodel) this->solightmodel->setOverride(TRUE);
 
451
  if (this->somaterialbinding) this->somaterialbinding->setOverride(TRUE);
 
452
#ifdef HAVE_SOPOLYGONOFFSET
 
453
  LOCATE_NODE(this->sopolygonoffset, SoPolygonOffset, "so@gui@->polygonoffset");
 
454
  if (this->sopolygonoffset) this->sopolygonoffset->setOverride(TRUE);
 
455
#endif // HAVE_SOPOLYGONOFFSET
 
456
 
 
457
#undef LOCATE_NODE
 
458
  this->searchaction->reset();
 
459
 
 
460
  root->unrefNoDelete();
 
461
  return (SoSeparator *) root;
 
462
}
 
463
 
 
464
// FIXME: this method is unnecessary robust, and generally
 
465
// weird. 20020522 mortene.
 
466
SoSeparator *
 
467
So@Gui@ViewerP::createFPSSuperimposition(void)
 
468
{
 
469
  static const char * fpsSceneGraph[] =
 
470
  {
 
471
    "#Inventor V2.1 ascii",
 
472
    "",
 
473
    "Separator {",
 
474
    "  renderCaching OFF",
 
475
    "  renderCulling OFF",
 
476
    "  pickCulling OFF",
 
477
    "  boundingBoxCaching OFF",
 
478
    "}",
 
479
    NULL
 
480
  };
 
481
 
 
482
  int i, bufsize;
 
483
  for (i = bufsize = 0; fpsSceneGraph[i]; i++)
 
484
    bufsize += strlen(fpsSceneGraph[i]) + 1;
 
485
  char * buf = new char [bufsize + 1];
 
486
  for (i = bufsize = 0; fpsSceneGraph[i]; i++) {
 
487
    strcpy(buf + bufsize, fpsSceneGraph[i]);
 
488
    bufsize += strlen(fpsSceneGraph[i]);
 
489
    buf[bufsize] = '\n';
 
490
    bufsize++;
 
491
  }
 
492
  SoInput * input = new SoInput;
 
493
  input->setBuffer(buf, bufsize);
 
494
  SoNode * root = NULL;
 
495
  SbBool ok = SoDB::read(input, root);
 
496
  delete input;
 
497
  delete [] buf;
 
498
  if (!ok) {
 
499
    SoDebugError::post("So@Gui@Viewer::So@Gui@Viewer",
 
500
                       "couldn't create viewer superscene");
 
501
    return NULL;
 
502
  }
 
503
  assert(root->isOfType(SoSeparator::getClassTypeId()));
 
504
  root->ref();
 
505
 
 
506
  root->unrefNoDelete();
 
507
  return NULL;
 
508
  return (SoSeparator *) root;
 
509
}
 
510
 
 
511
// Returns the coordinate system the current camera is located in. If
 
512
// there are transformations before the camera in the scene graph,
 
513
// this must be considered before doing certain operations. \a matrix
 
514
// and \a inverse will not contain the transformations caused by the
 
515
// camera fields, only the transformations traversed before the camera
 
516
// in the scene graph.
 
517
void
 
518
So@Gui@ViewerP::getCameraCoordinateSystem(SoCamera * camera,
 
519
                                          SoNode * root,
 
520
                                          SbMatrix & matrix,
 
521
                                          SbMatrix & inverse)
 
522
{
 
523
  this->searchaction->reset();
 
524
  this->searchaction->setSearchingAll(TRUE);
 
525
  this->searchaction->setInterest(SoSearchAction::FIRST);
 
526
  this->searchaction->setNode(camera);
 
527
  this->searchaction->apply(root);
 
528
 
 
529
  matrix = inverse = SbMatrix::identity();
 
530
  if (this->searchaction->getPath()) {
 
531
    this->matrixaction->apply(this->searchaction->getPath());
 
532
    matrix = this->matrixaction->getMatrix();
 
533
    inverse = this->matrixaction->getInverse();
 
534
  }
 
535
  this->searchaction->reset();
 
536
}
 
537
 
 
538
// FIXME: this code is really not applying the correct solution,
 
539
// trying to convert between SoOrthographic::height and
 
540
// SoPerspectiveCamera::heightAngle. What we should really try to do
 
541
// is:
 
542
//
 
543
//       * when going from orthocam -> perspectivecam: set the
 
544
//       heightAngle field to it's default value (45ļæ½), and move
 
545
//       camera to a position where the scene/model would fill about
 
546
//       the same screenspace as it did in the orthocam
 
547
//
 
548
//       * when going from perspectivecam -> orthocam: keep the
 
549
//       current position, but tune the view-volume height so the
 
550
//       scene/model takes up about the same screenspace
 
551
//
 
552
// 20020522 mortene.
 
553
 
 
554
void
 
555
So@Gui@ViewerP::convertOrtho2Perspective(const SoOrthographicCamera * in,
 
556
                                         SoPerspectiveCamera * out)
 
557
{
 
558
  out->aspectRatio.setValue(in->aspectRatio.getValue());
 
559
  out->focalDistance.setValue(in->focalDistance.getValue());
 
560
  out->orientation.setValue(in->orientation.getValue());
 
561
  out->position.setValue(in->position.getValue());
 
562
  out->viewportMapping.setValue(in->viewportMapping.getValue());
 
563
  
 
564
  float focaldist = in->focalDistance.getValue();
 
565
 
 
566
  // focalDistance==0.0f happens for empty scenes.
 
567
  if (focaldist != 0.0f) {
 
568
    out->heightAngle = 2.0f * atan(in->height.getValue() / 2.0f / focaldist);
 
569
  }
 
570
  else {
 
571
    // 45ļæ½ is the default value of this field in SoPerspectiveCamera.
 
572
    out->heightAngle = (float)(M_PI / 4.0);
 
573
  }
 
574
}
 
575
 
 
576
void
 
577
So@Gui@ViewerP::convertPerspective2Ortho(const SoPerspectiveCamera * in,
 
578
                                         SoOrthographicCamera * out)
 
579
{
 
580
  out->aspectRatio.setValue(in->aspectRatio.getValue());
 
581
  out->focalDistance.setValue(in->focalDistance.getValue());
 
582
  out->orientation.setValue(in->orientation.getValue());
 
583
  out->position.setValue(in->position.getValue());
 
584
  out->viewportMapping.setValue(in->viewportMapping.getValue());
 
585
 
 
586
  float focaldist = in->focalDistance.getValue();
 
587
 
 
588
  out->height = 2 * focaldist * tan(in->heightAngle.getValue() / 2.0f);
 
589
}
 
590
 
 
591
void
 
592
So@Gui@ViewerP::reallyRedraw(const SbBool clearcol, const SbBool clearz)
 
593
{
 
594
  // Recalculate near/far planes. Must be done in reallyRedraw() --
 
595
  // not actualRedraw() -- so the clipping planes are correct even
 
596
  // when rendering multiple times with different camera settings.
 
597
  if (PUBLIC(this)->isAutoClipping()) { this->setClippingPlanes(); }
 
598
 
 
599
  if (this->drawAsHiddenLine()) {
 
600
 
 
601
    // First pass: render as filled, but with the background color.
 
602
 
 
603
    this->solightmodel->model.setIgnored(FALSE); // override as SoLightModel::BASE
 
604
    this->sodrawstyle->style.setIgnored(TRUE); // draw as-is filled/lines/points
 
605
    this->socomplexity->type.setIgnored(TRUE); // as-is rendering space
 
606
    this->socomplexity->value.setIgnored(TRUE); // as-is complexity on non-simple shapes
 
607
    // textureQuality field of socomplexity node is always 0.0
 
608
 
 
609
    this->sobasecolor->rgb.setValue(PUBLIC(this)->getBackgroundColor());
 
610
    this->sobasecolor->rgb.setIgnored(FALSE);
 
611
    this->somaterialbinding->value.setIgnored(FALSE); // override with OVERALL
 
612
    this->polygonoffsetparent->whichChild = SO_SWITCH_ALL;
 
613
 
 
614
    PUBLIC(this)->getSceneManager()->render(clearcol, clearz);
 
615
 
 
616
    // Second pass, render wireframe on top.
 
617
 
 
618
    this->sodrawstyle->style = SoDrawStyle::LINES;
 
619
    this->sodrawstyle->style.setIgnored(FALSE); // force lines
 
620
    this->sobasecolor->rgb.setIgnored(TRUE); // use as-is line colors
 
621
    this->somaterialbinding->value.setIgnored(TRUE); // as-is
 
622
    this->polygonoffsetparent->whichChild = SO_SWITCH_NONE;
 
623
 
 
624
    PUBLIC(this)->getSceneManager()->render(FALSE, FALSE);
 
625
 
 
626
    return;
 
627
  }
 
628
  if (this->drawAsWireframeOverlay()) {
 
629
    // First pass: render as-is, with polygon offset
 
630
 
 
631
    this->solightmodel->model.setIgnored(TRUE);
 
632
    this->somaterialbinding->value.setIgnored(TRUE);
 
633
    this->sobasecolor->rgb.setIgnored(TRUE);
 
634
    this->sodrawstyle->style.setIgnored(TRUE); // draw as-is filled/lines/points
 
635
    this->socomplexity->type.setIgnored(TRUE); // as-is rendering space
 
636
    this->socomplexity->value.setIgnored(TRUE); // as-is complexity on non-simple shapes
 
637
    this->socomplexity->textureQuality.setIgnored(TRUE);
 
638
 
 
639
    this->somaterialbinding->value.setIgnored(TRUE); // override with OVERALL
 
640
    this->polygonoffsetparent->whichChild = SO_SWITCH_ALL;
 
641
 
 
642
    PUBLIC(this)->getSceneManager()->render(clearcol, clearz);
 
643
 
 
644
    // Second pass, render wireframe on top.
 
645
    this->sobasecolor->rgb.setValue(this->wireframeoverlaycolor);
 
646
    this->sobasecolor->rgb.setIgnored(FALSE);
 
647
    this->somaterialbinding->value.setIgnored(FALSE); // override with OVERALL
 
648
 
 
649
    this->solightmodel->model.setIgnored(FALSE); // override as SoLightModel::BASE
 
650
    this->sodrawstyle->style = SoDrawStyle::LINES;
 
651
    this->sodrawstyle->style.setIgnored(FALSE); // force lines
 
652
    this->polygonoffsetparent->whichChild = SO_SWITCH_NONE;
 
653
    this->socomplexity->textureQuality.setIgnored(FALSE);
 
654
 
 
655
    PUBLIC(this)->getSceneManager()->render(FALSE, FALSE);
 
656
 
 
657
    // disable override nodes
 
658
    (void) this->sobasecolor->rgb.enableNotify(FALSE);
 
659
    this->sobasecolor->rgb.setIgnored(TRUE);
 
660
    (void) this->sobasecolor->rgb.enableNotify(TRUE);
 
661
 
 
662
    (void) this->somaterialbinding->value.enableNotify(FALSE);
 
663
    this->somaterialbinding->value.setIgnored(TRUE);
 
664
    (void) this->somaterialbinding->value.enableNotify(TRUE);
 
665
 
 
666
    (void) this->solightmodel->model.enableNotify(FALSE);
 
667
    this->solightmodel->model.setIgnored(TRUE);
 
668
    (void) this->solightmodel->model.enableNotify(TRUE);
 
669
 
 
670
    (void) this->socomplexity->textureQuality.enableNotify(FALSE);
 
671
    this->socomplexity->textureQuality.setIgnored(TRUE);
 
672
    (void) this->socomplexity->textureQuality.enableNotify(TRUE);
 
673
 
 
674
    (void) this->sodrawstyle->style.enableNotify(FALSE);
 
675
    this->sodrawstyle->style.setIgnored(TRUE);
 
676
    (void) this->sodrawstyle->style.enableNotify(TRUE);
 
677
    return;
 
678
  }
 
679
 
 
680
  SbBool clearzbuffer = TRUE;
 
681
  So@Gui@Viewer::DrawStyle style = this->currentDrawStyle();
 
682
  switch (style) {
 
683
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
684
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
685
  case So@Gui@Viewer::VIEW_BBOX:
 
686
    clearzbuffer = FALSE;
 
687
  default:
 
688
    break; // Include "default:" case to avoid compiler warning.
 
689
  }
 
690
 
 
691
  PUBLIC(this)->getSceneManager()->render(clearcol, clearzbuffer && clearz);
 
692
}
 
693
 
 
694
 
 
695
// *************************************************************************
 
696
 
 
697
// Returns a boolean to indicate if the dynamic drawstyle equals
 
698
// the static drawstyle.
 
699
 
 
700
SbBool
 
701
So@Gui@ViewerP::drawInteractiveAsStill(void) const
 
702
{
 
703
  SbBool moveasstill = this->drawstyles[So@Gui@Viewer::INTERACTIVE] == So@Gui@Viewer::VIEW_SAME_AS_STILL;
 
704
  if (! moveasstill)
 
705
    moveasstill = this->drawstyles[So@Gui@Viewer::INTERACTIVE] == this->drawstyles[So@Gui@Viewer::STILL];
 
706
  if (! moveasstill)
 
707
    moveasstill =
 
708
      this->drawstyles[So@Gui@Viewer::INTERACTIVE] == So@Gui@Viewer::VIEW_NO_TEXTURE &&
 
709
      this->drawstyles[So@Gui@Viewer::STILL] != So@Gui@Viewer::VIEW_AS_IS;
 
710
  return moveasstill;
 
711
}
 
712
 
 
713
// Returns the current drawing style.
 
714
So@Gui@Viewer::DrawStyle
 
715
So@Gui@ViewerP::currentDrawStyle(void) const
 
716
{
 
717
  SbBool interactivemode = PUBLIC(this)->getInteractiveCount() > 0 ? TRUE : FALSE;
 
718
 
 
719
  if (!interactivemode || this->drawInteractiveAsStill())
 
720
    return this->drawstyles[So@Gui@Viewer::STILL];
 
721
  else
 
722
    return this->drawstyles[So@Gui@Viewer::INTERACTIVE];
 
723
}
 
724
 
 
725
// Returns a boolean to indicate if the current drawstyle settings implies
 
726
// hidden line rendering.
 
727
SbBool
 
728
So@Gui@ViewerP::drawAsHiddenLine(void) const
 
729
{
 
730
  return ((this->currentDrawStyle() == So@Gui@Viewer::VIEW_HIDDEN_LINE) ? TRUE : FALSE);
 
731
}
 
732
 
 
733
// Returns a boolean to indicate if the current drawstyle settings
 
734
// implies wirefram overlay rendering.
 
735
SbBool
 
736
So@Gui@ViewerP::drawAsWireframeOverlay(void) const
 
737
{
 
738
  return ((this->currentDrawStyle() == So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY) ? TRUE : FALSE);
 
739
}
 
740
 
 
741
// Use the given style setting to set the correct states in the
 
742
// rendering control nodes. This will affect the way the scene is
 
743
// currently rendered.
 
744
void
 
745
So@Gui@ViewerP::changeDrawStyle(So@Gui@Viewer::DrawStyle style)
 
746
{
 
747
  // Turn on/off Z-buffering based on the style setting.
 
748
  switch (style) {
 
749
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
750
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
751
  case So@Gui@Viewer::VIEW_BBOX:
 
752
    PUBLIC(this)->glLockNormal();
 
753
    // FIXME: shouldn't this be done "lazy", i.e. before we do any
 
754
    // actual rendering? 20001126 mortene.
 
755
    glDisable(GL_DEPTH_TEST);
 
756
    PUBLIC(this)->glUnlockNormal();
 
757
    break;
 
758
 
 
759
  default:
 
760
    PUBLIC(this)->glLockNormal();
 
761
    // FIXME: shouldn't this be done "lazy", i.e. before we do any
 
762
    // actual rendering? 20001126 mortene.
 
763
    glEnable(GL_DEPTH_TEST);
 
764
    PUBLIC(this)->glUnlockNormal();
 
765
    break;
 
766
  }
 
767
 
 
768
  // Render everything as its supposed to be done, don't override
 
769
  // any of the settings in the ``real'' graph.
 
770
  if (style == So@Gui@Viewer::VIEW_AS_IS) {
 
771
    this->drawstyleroot->whichChild = SO_SWITCH_NONE;
 
772
    return;
 
773
  }
 
774
 
 
775
  this->drawstyleroot->whichChild = SO_SWITCH_ALL;
 
776
  if ((style == So@Gui@Viewer::VIEW_HIDDEN_LINE) ||
 
777
      (style == So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY)) {
 
778
    this->hiddenlineroot->whichChild = SO_SWITCH_ALL;
 
779
    return;
 
780
  } else {
 
781
    this->hiddenlineroot->whichChild = SO_SWITCH_NONE;
 
782
  }
 
783
 
 
784
  // Set or unset lightmodel override.
 
785
  switch (style) {
 
786
  case So@Gui@Viewer::VIEW_NO_TEXTURE:
 
787
  case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
 
788
    this->solightmodel->model.setIgnored(TRUE); // as-is BASE or PHONG
 
789
    break;
 
790
 
 
791
  case So@Gui@Viewer::VIEW_LINE:
 
792
  case So@Gui@Viewer::VIEW_POINT:
 
793
  case So@Gui@Viewer::VIEW_BBOX:
 
794
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
795
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
796
    this->solightmodel->model.setIgnored(FALSE); // force BASE lighting
 
797
    break;
 
798
 
 
799
  default:
 
800
    assert(FALSE); break;
 
801
  }
 
802
 
 
803
 
 
804
  // Set or unset drawstyle override.
 
805
  switch (style) {
 
806
  case So@Gui@Viewer::VIEW_NO_TEXTURE:
 
807
  case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
 
808
    this->sodrawstyle->style.setIgnored(TRUE); // as-is drawing style filled/lines/points
 
809
    break;
 
810
 
 
811
  case So@Gui@Viewer::VIEW_LINE:
 
812
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
813
  case So@Gui@Viewer::VIEW_BBOX:
 
814
    this->sodrawstyle->style = SoDrawStyle::LINES;
 
815
    this->sodrawstyle->style.setIgnored(FALSE); // force line rendering
 
816
    break;
 
817
 
 
818
  case So@Gui@Viewer::VIEW_POINT:
 
819
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
820
    this->sodrawstyle->style = SoDrawStyle::POINTS;
 
821
    this->sodrawstyle->style.setIgnored(FALSE); // force point rendering
 
822
    break;
 
823
 
 
824
  default:
 
825
    assert(FALSE); break;
 
826
  }
 
827
 
 
828
  // Set or unset complexity value override.
 
829
  switch (style) {
 
830
  case So@Gui@Viewer::VIEW_NO_TEXTURE:
 
831
  case So@Gui@Viewer::VIEW_LINE:
 
832
  case So@Gui@Viewer::VIEW_POINT:
 
833
  case So@Gui@Viewer::VIEW_BBOX:
 
834
    this->socomplexity->value.setIgnored(TRUE); // as-is complexity
 
835
    break;
 
836
 
 
837
  case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
 
838
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
839
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
840
    this->socomplexity->value.setIgnored(FALSE); // force complexity setting of 0.1
 
841
    break;
 
842
 
 
843
  default:
 
844
    assert(FALSE); break;
 
845
  }
 
846
 
 
847
  // Set or unset complexity textureQuality override (the value of the
 
848
  // override-field is always 0.0, ie signalling "textures off").
 
849
  switch (style) {
 
850
  case So@Gui@Viewer::VIEW_HIDDEN_LINE:
 
851
  case So@Gui@Viewer::VIEW_NO_TEXTURE:
 
852
  case So@Gui@Viewer::VIEW_LINE:
 
853
  case So@Gui@Viewer::VIEW_POINT:
 
854
  case So@Gui@Viewer::VIEW_BBOX:
 
855
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
856
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
857
    this->socomplexity->textureQuality.setIgnored(FALSE); // textures off
 
858
    break;
 
859
 
 
860
  default:
 
861
    this->socomplexity->textureQuality.setIgnored(TRUE); // don't override
 
862
    break;
 
863
  }
 
864
 
 
865
  // Set or unset complexity type override.
 
866
  switch (style) {
 
867
  case So@Gui@Viewer::VIEW_NO_TEXTURE:
 
868
  case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
 
869
  case So@Gui@Viewer::VIEW_LINE:
 
870
  case So@Gui@Viewer::VIEW_POINT:
 
871
  case So@Gui@Viewer::VIEW_LOW_RES_LINE:
 
872
  case So@Gui@Viewer::VIEW_LOW_RES_POINT:
 
873
    this->socomplexity->type.setIgnored(TRUE); // as-is
 
874
    break;
 
875
 
 
876
  case So@Gui@Viewer::VIEW_BBOX:
 
877
    this->socomplexity->type = SoComplexity::BOUNDING_BOX;
 
878
    this->socomplexity->type.setIgnored(FALSE); // force bounding box rendering
 
879
    break;
 
880
 
 
881
  default:
 
882
    assert(FALSE); break;
 
883
  }
 
884
 
 
885
#if 0 // debug
 
886
  SoDebugError::postInfo("So@Gui@Viewer::changeDrawStyle",
 
887
                         "\n"
 
888
                         "\tdrawstyle style: 0x%02x (isIgnored() == %s)\n"
 
889
                         "\tlightmodel model: 0x%02x, (isIgnored() == %s)\n"
 
890
                         "\tcomplexity type: 0x%02x, (isIgnored() == %s)\n"
 
891
                         "\tcomplexity value: %f, (isIgnored() == %s)\n"
 
892
                         "",
 
893
                         this->sodrawstyle->style.getValue(),
 
894
                         this->sodrawstyle->style.isIgnored() ? "T" : "F",
 
895
                         this->solightmodel->model.getValue(),
 
896
                         this->solightmodel->model.isIgnored() ? "T" : "F",
 
897
                         this->socomplexity->type.getValue(),
 
898
                         this->socomplexity->type.isIgnored() ? "T" : "F",
 
899
                         this->socomplexity->value.getValue(),
 
900
                         this->socomplexity->value.isIgnored() ? "T" : "F");
 
901
#endif // debug
 
902
}
 
903
 
 
904
// Position the near and far clipping planes just in front of and
 
905
// behind the scene's bounding box. This will give us the optimal
 
906
// utilization of the z buffer resolution by shrinking it to its
 
907
// minimum depth.
 
908
//
 
909
// Near and far clipping planes are specified in the camera fields
 
910
// nearDistance and farDistance.
 
911
void
 
912
So@Gui@ViewerP::setClippingPlanes(void)
 
913
{
 
914
  // This is necessary to avoid a crash in case there is no scene
 
915
  // graph specified by the user.
 
916
  if (this->camera == NULL) return;
 
917
 
 
918
  if (this->autoclipbboxaction == NULL)
 
919
    this->autoclipbboxaction =
 
920
      new SoGetBoundingBoxAction(PUBLIC(this)->getViewportRegion());
 
921
  else
 
922
    this->autoclipbboxaction->setViewportRegion(PUBLIC(this)->getViewportRegion());
 
923
 
 
924
  this->autoclipbboxaction->apply(this->sceneroot);
 
925
 
 
926
  SbXfBox3f xbox = this->autoclipbboxaction->getXfBoundingBox();
 
927
 
 
928
  SbMatrix cammat;
 
929
  SbMatrix inverse;
 
930
  this->getCameraCoordinateSystem(this->camera, this->sceneroot, cammat, inverse);
 
931
  xbox.transform(inverse);
 
932
 
 
933
  SbMatrix mat;
 
934
  mat.setTranslate(- this->camera->position.getValue());
 
935
  xbox.transform(mat);
 
936
  mat = this->camera->orientation.getValue().inverse();
 
937
  xbox.transform(mat);
 
938
  SbBox3f box = xbox.project();
 
939
 
 
940
  // Bounding box was calculated in camera space, so we need to "flip"
 
941
  // the box (because camera is pointing in the (0,0,-1) direction
 
942
  // from origo.
 
943
  float nearval = -box.getMax()[2];
 
944
  float farval = -box.getMin()[2];
 
945
 
 
946
  // FIXME: what if we have a weird scale transform in the scenegraph?
 
947
  // Could we end up with nearval > farval then? Investigate, then
 
948
  // either use an assert() (if it can't happen) or an So@Gui@Swap()
 
949
  // (to handle it). 20020116 mortene.
 
950
 
 
951
  // Check if scene is completely behind us.
 
952
  if (farval <= 0.0f) { return; }
 
953
 
 
954
  if (this->camera->isOfType(SoPerspectiveCamera::getClassTypeId())) {
 
955
    // Disallow negative and small near clipping plane distance.
 
956
    
 
957
    float nearlimit; // the smallest value allowed for nearval
 
958
    if (this->autoclipstrategy == So@Gui@Viewer::CONSTANT_NEAR_PLANE) {
 
959
      nearlimit = this->autoclipvalue;
 
960
    }
 
961
    else {
 
962
      assert(this->autoclipstrategy == So@Gui@Viewer::VARIABLE_NEAR_PLANE);
 
963
      // From glFrustum() documentation: Depth-buffer precision is
 
964
      // affected by the values specified for znear and zfar. The
 
965
      // greater the ratio of zfar to znear is, the less effective the
 
966
      // depth buffer will be at distinguishing between surfaces that
 
967
      // are near each other. If r = far/near, roughly log (2) r bits
 
968
      // of depth buffer precision are lost. Because r approaches
 
969
      // infinity as znear approaches zero, you should never set znear
 
970
      // to zero.
 
971
 
 
972
      GLint depthbits[1];
 
973
      glGetIntegerv(GL_DEPTH_BITS, depthbits);
 
974
      
 
975
      int use_bits = (int) (float(depthbits[0]) * (1.0f-this->autoclipvalue)); 
 
976
      float r = (float) pow(2.0, (double) use_bits);
 
977
      nearlimit = farval / r;
 
978
    }
 
979
    
 
980
    if (nearlimit >= farval) {
 
981
      // (The "5000" magic constant was found by fiddling around a bit
 
982
      // on an OpenGL implementation with a 16-bit depth-buffer
 
983
      // resolution, adjusting to find something that would work well
 
984
      // with both a very "stretched" / deep scene and a more compact
 
985
      // single-model one.)
 
986
      nearlimit = farval / 5000.0f;
 
987
    }
 
988
    
 
989
    // adjust the near plane if the the value is too small.
 
990
    if (nearval < nearlimit) {
 
991
      nearval = nearlimit;
 
992
    }
 
993
    
 
994
    if (this->autoclipcb) {
 
995
      SbVec2f nearfar;
 
996
      nearfar[0] = nearval;
 
997
      nearfar[1] = farval;
 
998
      
 
999
      nearfar = this->autoclipcb(this->autoclipuserdata, nearfar);
 
1000
      
 
1001
      nearval = nearfar[0];
 
1002
      farval = nearfar[1]; 
 
1003
    }
 
1004
  }
 
1005
 
 
1006
  // Some slack around the bounding box, in case the scene fits
 
1007
  // exactly inside it. This is done to minimize the chance of
 
1008
  // artifacts caused by the limitation of the z-buffer
 
1009
  // resolution. One common artifact if this is not done is that the
 
1010
  // near clipping plane cuts into the corners of the model as it's
 
1011
  // rotated.
 
1012
  const float SLACK = 0.001f;
 
1013
 
 
1014
  this->camera->nearDistance = nearval * (1.0f - SLACK);
 
1015
  this->camera->farDistance = farval * (1.0f + SLACK);
 
1016
 
 
1017
 
 
1018
  // FIXME: there's a possible optimization to take advantage of here,
 
1019
  // since we are able to sometimes know for sure that all geometry is
 
1020
  // completely inside the view volume. I quote from the "OpenGL FAQ
 
1021
  // and Troubleshooting Guide":
 
1022
  //
 
1023
  //  "10.050 I know my geometry is inside the view volume. How can I
 
1024
  //  turn off OpenGL's view-volume clipping to maximize performance?
 
1025
  //
 
1026
  //   Standard OpenGL doesn't provide a mechanism to disable the
 
1027
  //   view-volume clipping test; thus, it will occur for every
 
1028
  //   primitive you send.
 
1029
  //
 
1030
  //   Some implementations of OpenGL support the
 
1031
  //   GL_EXT_clip_volume_hint extension. If the extension is
 
1032
  //   available, a call to
 
1033
  //   glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT,GL_FASTEST) will inform
 
1034
  //   OpenGL that the geometry is entirely within the view volume and
 
1035
  //   that view-volume clipping is unnecessary. Normal clipping can
 
1036
  //   be resumed by setting this hint to GL_DONT_CARE. When clipping
 
1037
  //   is disabled with this hint, results are undefined if geometry
 
1038
  //   actually falls outside the view volume."
 
1039
  //
 
1040
  // 20020117 mortene.
 
1041
 
 
1042
 
 
1043
  if (SO@GUI@_DEBUG && 0) { // debug
 
1044
    SoDebugError::postInfo("So@Gui@Viewer::setClippingPlanes",
 
1045
                           "near, far: %f (%f), %f (%f)",
 
1046
                           nearval, this->camera->nearDistance.getValue(),
 
1047
                           farval, this->camera->farDistance.getValue());
 
1048
  }
 
1049
}
 
1050
 
 
1051
// Translate camera a distance equal to the difference in projected,
 
1052
// normalized screen coordinates given by the argument.
 
1053
void
 
1054
So@Gui@ViewerP::moveCameraScreen(const SbVec2f & screenpos)
 
1055
{
 
1056
  SoCamera * cam = PUBLIC(this)->getCamera();
 
1057
  assert(cam);
 
1058
 
 
1059
  if (SO@GUI@_DEBUG && 0) { // debug
 
1060
    SoDebugError::postInfo("So@Gui@Viewer::moveCameraScreen",
 
1061
                           "screenpos: <%f, %f>, campos: <%f, %f, %f>",
 
1062
                           screenpos[0], screenpos[1],
 
1063
                           cam->position.getValue()[0],
 
1064
                           cam->position.getValue()[1],
 
1065
                           cam->position.getValue()[2]);
 
1066
  }
 
1067
 
 
1068
  SbViewVolume vv = cam->getViewVolume(PUBLIC(this)->getGLAspectRatio());
 
1069
  SbPlane panplane = vv.getPlane(cam->focalDistance.getValue());
 
1070
 
 
1071
  SbLine line;
 
1072
  vv.projectPointToLine(screenpos + SbVec2f(0.5, 0.5f), line);
 
1073
  SbVec3f current_planept;
 
1074
  panplane.intersect(line, current_planept);
 
1075
  vv.projectPointToLine(SbVec2f(0.5f, 0.5f), line);
 
1076
  SbVec3f old_planept;
 
1077
  panplane.intersect(line, old_planept);
 
1078
 
 
1079
  // Reposition camera according to the vector difference between the
 
1080
  // projected points.
 
1081
  cam->position = cam->position.getValue() - (current_planept - old_planept);
 
1082
 
 
1083
  if (SO@GUI@_DEBUG && 0) { // debug
 
1084
    SoDebugError::postInfo("So@Gui@Viewer::moveCameraScreen",
 
1085
                           "newcampos: <%f, %f, %f>",
 
1086
                           cam->position.getValue()[0],
 
1087
                           cam->position.getValue()[1],
 
1088
                           cam->position.getValue()[2]);
 
1089
  }
 
1090
}
 
1091
 
 
1092
// Called when viewer enters interactive mode (animation, drag, ...).
 
1093
void
 
1094
So@Gui@ViewerP::interactivestartCB(void *, So@Gui@Viewer * thisp)
 
1095
{
 
1096
  // In interactive buffer mode, doublebuffering is used during interaction.
 
1097
  if (PRIVATE(thisp)->buffertype == So@Gui@Viewer::BUFFER_INTERACTIVE) {
 
1098
    PRIVATE(thisp)->localsetbuffertype = TRUE;
 
1099
    thisp->So@Gui@RenderArea::setDoubleBuffer(TRUE);
 
1100
    PRIVATE(thisp)->localsetbuffertype = FALSE;
 
1101
  }
 
1102
 
 
1103
  // Use the dynamic drawstyle.
 
1104
  if (!PRIVATE(thisp)->drawInteractiveAsStill())
 
1105
    PRIVATE(thisp)->changeDrawStyle(PRIVATE(thisp)->drawstyles[So@Gui@Viewer::INTERACTIVE]);
 
1106
}
 
1107
 
 
1108
// Called when viewer goes out of interactive mode and into "frozen"
 
1109
// mode.
 
1110
void
 
1111
So@Gui@ViewerP::interactiveendCB(void *, So@Gui@Viewer * thisp)
 
1112
{
 
1113
  // In interactive buffer mode, doublebuffering is used during
 
1114
  // interaction, singelbuffering while the camera is static.
 
1115
  if (PRIVATE(thisp)->buffertype == So@Gui@Viewer::BUFFER_INTERACTIVE) {
 
1116
    PRIVATE(thisp)->localsetbuffertype = TRUE;
 
1117
    thisp->So@Gui@RenderArea::setDoubleBuffer(FALSE);
 
1118
    PRIVATE(thisp)->localsetbuffertype = FALSE;
 
1119
  }
 
1120
 
 
1121
  // Back to static drawstyle.
 
1122
  if (!PRIVATE(thisp)->drawInteractiveAsStill())
 
1123
    PRIVATE(thisp)->changeDrawStyle(PRIVATE(thisp)->drawstyles[So@Gui@Viewer::STILL]);
 
1124
}
 
1125
 
 
1126
// Called repeatedly during the seek animation.
 
1127
void
 
1128
So@Gui@ViewerP::seeksensorCB(void * data, SoSensor * s)
 
1129
{
 
1130
  SbTime currenttime = SbTime::getTimeOfDay();
 
1131
 
 
1132
  So@Gui@Viewer * thisp = (So@Gui@Viewer *)data;
 
1133
  SoTimerSensor * sensor = (SoTimerSensor *)s;
 
1134
 
 
1135
  float t =
 
1136
    (currenttime - sensor->getBaseTime()).getValue() / PRIVATE(thisp)->seekperiod;
 
1137
  if ((t > 1.0f) || (t + sensor->getInterval().getValue() > 1.0f)) t = 1.0f;
 
1138
  SbBool end = (t == 1.0f);
 
1139
  
 
1140
  t = (float) ((1.0 - cos(M_PI*t)) * 0.5);
 
1141
  
 
1142
  PRIVATE(thisp)->camera->position = PRIVATE(thisp)->camerastartposition +
 
1143
    (PRIVATE(thisp)->cameraendposition - PRIVATE(thisp)->camerastartposition) * t;
 
1144
  PRIVATE(thisp)->camera->orientation = 
 
1145
    SbRotation::slerp(PRIVATE(thisp)->camerastartorient,
 
1146
                      PRIVATE(thisp)->cameraendorient, 
 
1147
                      t);
 
1148
 
 
1149
  if (end) thisp->setSeekMode(FALSE);
 
1150
}
 
1151
 
 
1152
// Reset the frames-per-second counter upon window resize events,
 
1153
// abnormal delays, etc.
 
1154
//
 
1155
// The methods for recording FPS values are Coin extensions, not
 
1156
// available in the original Open Inventor API.
 
1157
//
 
1158
// \sa addFrametime(), recordFPS()
 
1159
void
 
1160
So@Gui@ViewerP::resetFrameCounter(void)
 
1161
{
 
1162
  this->framecount = 0;
 
1163
  for (int i = 0; i < So@Gui@ViewerP::FRAMESARRAY_SIZE; i++)
 
1164
    this->frames[i] = SbVec2f(0.0f, 0.0f);
 
1165
  this->totalcoin = 0.0f;
 
1166
  this->totaldraw = 0.0f;
 
1167
  this->lastgettimeofday = SbTime::getTimeOfDay().getValue();
 
1168
}
 
1169
 
 
1170
// Adds the time spent drawing the last frame to the array of past
 
1171
// frame times. Returns the current averaged fps-value.
 
1172
//
 
1173
// The methods for recording FPS values are Coin extensions, not
 
1174
// available in the original Open Inventor API.
 
1175
//
 
1176
// \sa resetFrameCounter(), recordFPS()
 
1177
SbVec2f
 
1178
So@Gui@ViewerP::addFrametime(const double ft)
 
1179
{
 
1180
  this->framecount++;
 
1181
 
 
1182
  int arrayptr = (this->framecount - 1) % FRAMESARRAY_SIZE;
 
1183
 
 
1184
  this->totalcoin += (ft - this->frames[arrayptr][0]);
 
1185
  float coinfps =
 
1186
    this->totalcoin / So@Gui@Min(this->framecount, (int) FRAMESARRAY_SIZE);
 
1187
 
 
1188
  double timeofday = SbTime::getTimeOfDay().getValue();
 
1189
  double ct = timeofday - this->lastgettimeofday;
 
1190
  this->totaldraw += (ct - this->frames[arrayptr][1]);
 
1191
  float drawfps =
 
1192
    this->totaldraw / So@Gui@Min(this->framecount, (int) FRAMESARRAY_SIZE);
 
1193
 
 
1194
  this->frames[arrayptr] = SbVec2f(ft, ct);
 
1195
  this->lastgettimeofday = timeofday;
 
1196
 
 
1197
  return SbVec2f(1.0f / coinfps, 1.0f / drawfps);
 
1198
}
 
1199
 
 
1200
static unsigned char fps2dfont[][12] = {
 
1201
  {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, //
 
1202
  {  0,  0, 12, 12,  0,  8, 12, 12, 12, 12, 12,  0 }, // !
 
1203
  {  0,  0,  0,  0,  0,  0,  0,  0,  0, 20, 20, 20 }, // \"
 
1204
  {  0,  0, 18, 18, 18, 63, 18, 18, 63, 18, 18,  0 }, // #
 
1205
  {  0,  8, 28, 42, 10, 10, 12, 24, 40, 42, 28,  8 }, // $
 
1206
  {  0,  0,  6, 73, 41, 22,  8, 52, 74, 73, 48,  0 }, // %
 
1207
  {  0, 12, 18, 18, 12, 25, 37, 34, 34, 29,  0,  0 }, // &
 
1208
  { 12, 12, 24,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, // '
 
1209
  {  0,  6,  8,  8, 16, 16, 16, 16, 16,  8,  8,  6 }, // (
 
1210
  {  0, 48,  8,  8,  4,  4,  4,  4,  4,  8,  8, 48 }, //)
 
1211
  {  0,  0,  0,  0,  0,  0,  8, 42, 20, 42,  8,  0 }, // *
 
1212
  {  0,  0,  0,  8,  8,  8,127,  8,  8,  8,  0,  0 }, // +
 
1213
  {  0, 24, 12, 12,  0,  0,  0,  0,  0,  0,  0,  0 }, // ,
 
1214
  {  0,  0,  0,  0,  0,  0,127,  0,  0,  0,  0,  0 }, // -
 
1215
  {  0,  0, 24, 24,  0,  0,  0,  0,  0,  0,  0,  0 }, // .
 
1216
  {  0, 32, 32, 16, 16,  8,  8,  8,  4,  4,  2,  2 }, // /
 
1217
  {  0,  0, 28, 34, 34, 34, 34, 34, 34, 34, 28,  0 }, // 0
 
1218
  {  0,  0,  8,  8,  8,  8,  8,  8, 40, 24,  8,  0 }, // 1
 
1219
  {  0,  0, 62, 32, 16,  8,  4,  2,  2, 34, 28,  0 }, // 2
 
1220
  {  0,  0, 28, 34,  2,  2, 12,  2,  2, 34, 28,  0 }, // 3
 
1221
  {  0,  0,  4,  4,  4,126, 68, 36, 20, 12,  4,  0 }, // 4
 
1222
  {  0,  0, 28, 34,  2,  2,  2, 60, 32, 32, 62,  0 }, // 5
 
1223
  {  0,  0, 28, 34, 34, 34, 60, 32, 32, 34, 28,  0 }, // 6
 
1224
  {  0,  0, 16, 16, 16,  8,  8,  4,  2,  2, 62,  0 }, // 7
 
1225
  {  0,  0, 28, 34, 34, 34, 28, 34, 34, 34, 28,  0 }, // 8
 
1226
  {  0,  0, 28, 34,  2,  2, 30, 34, 34, 34, 28,  0 }, // 9
 
1227
  {  0,  0, 24, 24,  0,  0,  0, 24, 24,  0,  0,  0 }, // :
 
1228
  {  0, 48, 24, 24,  0,  0,  0, 24, 24,  0,  0,  0 }, // ;
 
1229
  {  0,  0,  0,  2,  4,  8, 16,  8,  4,  2,  0,  0 }, // <
 
1230
  {  0,  0,  0,  0,  0,127,  0,127,  0,  0,  0,  0 }, // =
 
1231
  {  0,  0,  0, 16,  8,  4,  2,  4,  8, 16,  0,  0 }, // >
 
1232
  {  0,  0, 16, 16,  0, 16, 28,  2,  2,  2, 60,  0 }, // ?
 
1233
  {  0,  0, 28, 32, 73, 86, 82, 82, 78, 34, 28,  0 }, // @
 
1234
  {  0,  0, 33, 33, 33, 63, 18, 18, 18, 12, 12,  0 }, // A
 
1235
  {  0,  0, 60, 34, 34, 34, 60, 34, 34, 34, 60,  0 }, // B
 
1236
  {  0,  0, 14, 16, 32, 32, 32, 32, 32, 18, 14,  0 }, // C
 
1237
  {  0,  0, 56, 36, 34, 34, 34, 34, 34, 36, 56,  0 }, // D
 
1238
  {  0,  0, 62, 32, 32, 32, 60, 32, 32, 32, 62,  0 }, // E
 
1239
  {  0,  0, 16, 16, 16, 16, 30, 16, 16, 16, 30,  0 }, // F
 
1240
  {  0,  0, 14, 18, 34, 34, 32, 32, 32, 18, 14,  0 }, // G
 
1241
  {  0,  0, 34, 34, 34, 34, 62, 34, 34, 34, 34,  0 }, // H
 
1242
  {  0,  0, 62,  8,  8,  8,  8,  8,  8,  8, 62,  0 }, // I
 
1243
  {  0,  0,112,  8,  8,  8,  8,  8,  8,  8, 62,  0 }, // J
 
1244
  {  0,  0, 33, 33, 34, 36, 56, 40, 36, 34, 33,  0 }, // K
 
1245
  {  0,  0, 30, 16, 16, 16, 16, 16, 16, 16, 16,  0 }, // L
 
1246
  {  0,  0, 33, 33, 33, 45, 45, 45, 51, 51, 33,  0 }, // M
 
1247
  {  0,  0, 34, 34, 38, 38, 42, 42, 50, 50, 34,  0 }, // N
 
1248
  {  0,  0, 12, 18, 33, 33, 33, 33, 33, 18, 12,  0 }, // O
 
1249
  {  0,  0, 32, 32, 32, 60, 34, 34, 34, 34, 60,  0 }, // P
 
1250
  {  3,  6, 12, 18, 33, 33, 33, 33, 33, 18, 12,  0 }, // Q
 
1251
  {  0,  0, 34, 34, 34, 36, 60, 34, 34, 34, 60,  0 }, // R
 
1252
  {  0,  0, 60,  2,  2,  6, 28, 48, 32, 32, 30,  0 }, // S
 
1253
  {  0,  0,  8,  8,  8,  8,  8,  8,  8,  8,127,  0 }, // T
 
1254
  {  0,  0, 28, 34, 34, 34, 34, 34, 34, 34, 34,  0 }, // U
 
1255
  {  0,  0, 12, 12, 18, 18, 18, 33, 33, 33, 33,  0 }, // V
 
1256
  {  0,  0, 34, 34, 34, 54, 85, 73, 73, 73, 65,  0 }, // W
 
1257
  {  0,  0, 34, 34, 20, 20,  8, 20, 20, 34, 34,  0 }, // X
 
1258
  {  0,  0,  8,  8,  8,  8, 20, 20, 34, 34, 34,  0 }, // Y
 
1259
  {  0,  0, 62, 32, 16, 16,  8,  4,  4,  2, 62,  0 }, // Z
 
1260
  {  0, 14,  8,  8,  8,  8,  8,  8,  8,  8,  8, 14 }, // [
 
1261
  {  0,  2,  2,  4,  4,  8,  8,  8, 16, 16, 32, 32 }, // [backslash]
 
1262
  {  0, 56,  8,  8,  8,  8,  8,  8,  8,  8,  8, 56 }, // ]
 
1263
  {  0,  0,  0,  0,  0, 34, 34, 20, 20,  8,  8,  0 }, // ^
 
1264
  {  0,127,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, // _
 
1265
  {  0,  0,  0,  0,  0,  0,  0,  0,  0, 24, 24, 12 }, // `
 
1266
  {  0,  0, 29, 34, 34, 30,  2, 34, 28,  0,  0,  0 }, // a
 
1267
  {  0,  0, 60, 34, 34, 34, 34, 50, 44, 32, 32, 32 }, // b
 
1268
  {  0,  0, 14, 16, 32, 32, 32, 16, 14,  0,  0,  0 }, // c
 
1269
  {  0,  0, 26, 38, 34, 34, 34, 34, 30,  2,  2,  2 }, // d
 
1270
  {  0,  0, 28, 34, 32, 62, 34, 34, 28,  0,  0,  0 }, // e
 
1271
  {  0,  0, 16, 16, 16, 16, 16, 16, 62, 16, 16, 14 }, // f
 
1272
  { 28,  2,  2, 26, 38, 34, 34, 34, 30,  0,  0,  0 }, // g
 
1273
  {  0,  0, 34, 34, 34, 34, 34, 50, 44, 32, 32, 32 }, // h
 
1274
  {  0,  0,  8,  8,  8,  8,  8,  8, 56,  0,  8,  8 }, // i
 
1275
  { 56,  4,  4,  4,  4,  4,  4,  4, 60,  0,  4,  4 }, // j
 
1276
  {  0,  0, 33, 34, 36, 56, 40, 36, 34, 32, 32, 32 }, // k
 
1277
  {  0,  0,  8,  8,  8,  8,  8,  8,  8,  8,  8, 56 }, // l
 
1278
  {  0,  0, 73, 73, 73, 73, 73,109, 82,  0,  0,  0 }, // m
 
1279
  {  0,  0, 34, 34, 34, 34, 34, 50, 44,  0,  0,  0 }, // n
 
1280
  {  0,  0, 28, 34, 34, 34, 34, 34, 28,  0,  0,  0 }, // o
 
1281
  { 32, 32, 60, 34, 34, 34, 34, 50, 44,  0,  0,  0 }, // p
 
1282
  {  2,  2, 26, 38, 34, 34, 34, 34, 30,  0,  0,  0 }, // q
 
1283
  {  0,  0, 16, 16, 16, 16, 16, 24, 22,  0,  0,  0 }, // r
 
1284
  {  0,  0, 60,  2,  2, 28, 32, 32, 30,  0,  0,  0 }, // s
 
1285
  {  0,  0, 14, 16, 16, 16, 16, 16, 62, 16, 16,  0 }, // t
 
1286
  {  0,  0, 26, 38, 34, 34, 34, 34, 34,  0,  0,  0 }, // u
 
1287
  {  0,  0,  8,  8, 20, 20, 34, 34, 34,  0,  0,  0 }, // v
 
1288
  {  0,  0, 34, 34, 34, 85, 73, 73, 65,  0,  0,  0 }, // w
 
1289
  {  0,  0, 34, 34, 20,  8, 20, 34, 34,  0,  0,  0 }, // x
 
1290
  { 48, 16,  8,  8, 20, 20, 34, 34, 34,  0,  0,  0 }, // y
 
1291
  {  0,  0, 62, 32, 16,  8,  4,  2, 62,  0,  0,  0 }, // z
 
1292
  {  0,  6,  8,  8,  8,  4, 24,  4,  8,  8,  8,  6 }, // {
 
1293
  {  0,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8 }, // |
 
1294
  {  0, 48,  8,  8,  8, 16, 12, 16,  8,  8,  8, 48 }, // }
 
1295
  {  0,  0,  0,  0,  0,  0, 78, 57,  0,  0,  0,  0 }  // ~
 
1296
};
 
1297
 
 
1298
static void
 
1299
printString(const char * s)
 
1300
{
 
1301
  int i,n;
 
1302
  n = strlen(s);
 
1303
  for (i = 0; i < n; i++)
 
1304
    glBitmap(8, 12, 0.0, 2.0, 10.0, 0.0, fps2dfont[s[i] - 32]);
 
1305
}
 
1306
 
 
1307
static void
 
1308
Draw2DString(const char * str, SbVec2s glsize, SbVec2f position)
 
1309
{
 
1310
  // Store GL state.
 
1311
  glPushAttrib(GL_ALL_ATTRIB_BITS);
 
1312
 
 
1313
  glDisable(GL_LIGHTING);
 
1314
  glDisable(GL_DEPTH_TEST);
 
1315
  glDisable(GL_TEXTURE_2D);
 
1316
  glMatrixMode(GL_MODELVIEW);
 
1317
  glPushMatrix();
 
1318
  glLoadIdentity();
 
1319
 
 
1320
  glMatrixMode(GL_PROJECTION);
 
1321
  glPushMatrix();
 
1322
  glLoadIdentity();
 
1323
  glOrtho(0.0, glsize[0], 0.0, glsize[1], -1, 1);
 
1324
 
 
1325
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
1326
 
 
1327
  glColor3f(0.0, 0.0, 0.0);
 
1328
  glRasterPos2f(position[0] + 1, position[1]);
 
1329
  printString(str);
 
1330
  glRasterPos2f(position[0] - 1, position[1]);
 
1331
  printString(str);
 
1332
  glRasterPos2f(position[0], position[1] + 1);
 
1333
  printString(str);
 
1334
  glRasterPos2f(position[0], position[1] - 1);
 
1335
  printString(str);
 
1336
 
 
1337
  glColor3f(1.0, 1.0, 0.0);
 
1338
  glRasterPos2f(position[0], position[1]);
 
1339
  printString(str);
 
1340
 
 
1341
  glMatrixMode(GL_PROJECTION);
 
1342
  glPopMatrix();
 
1343
  glMatrixMode(GL_MODELVIEW);
 
1344
  glPopMatrix();
 
1345
 
 
1346
  glPopAttrib();
 
1347
}
 
1348
 
 
1349
 
 
1350
// FIXME: the following is just a temporary hack to enable the FPS
 
1351
// counter. We should really write a proper interface against it, so
 
1352
// applications can set up feedback loops to control scene complexity
 
1353
// and get a nice and steady maximum framerate, for instance.
 
1354
//
 
1355
// For anyone who want to execute that task, check what TGS has done
 
1356
// first. If their API is fine, use the same approach.
 
1357
//
 
1358
// 20001124 mortene.
 
1359
 
 
1360
// Draw a text string showing the current frame-per-seconds value in
 
1361
// the lower left corner of the OpenGL canvas (after recording
 
1362
// information needed to calculate the fps).
 
1363
//
 
1364
// The methods for recording FPS values are Coin extensions, not
 
1365
// available in the original Open Inventor API.
 
1366
//
 
1367
// \sa resetFrameCounter(), addFrametime()
 
1368
void
 
1369
So@Gui@ViewerP::recordFPS(const double rendertime)
 
1370
{
 
1371
  if (COIN_SHOW_FPS_COUNTER == UNINITIALIZED_ENVVAR) {
 
1372
    const char * env = SoAny::si()->getenv("COIN_SHOW_FPS_COUNTER");
 
1373
    COIN_SHOW_FPS_COUNTER = env ? atoi(env) : 0;
 
1374
  }
 
1375
 
 
1376
  if (COIN_SHOW_FPS_COUNTER) {
 
1377
    SbVec2f fps = this->addFrametime(rendertime);
 
1378
 
 
1379
    char buffer[64];
 
1380
    int nr = sprintf(buffer, "%.1f/%.1f fps", fps[0], fps[1]);
 
1381
    assert(nr < 64);
 
1382
    Draw2DString(buffer, PUBLIC(this)->getGLSize(), SbVec2f(10, 10));
 
1383
  }
 
1384
}
 
1385
 
 
1386
#endif // DOXYGEN_SKIP_THIS
 
1387
 
 
1388
// *************************************************************************
 
1389
 
 
1390
SO@GUI@_OBJECT_ABSTRACT_SOURCE(So@Gui@Viewer);
 
1391
 
 
1392
// *************************************************************************
 
1393
 
 
1394
/*!
 
1395
  \enum So@Gui@Viewer::Type
 
1396
 
 
1397
  Hints about what context the viewer will be used in.  Usually not
 
1398
  very interesting for the application programmer, it doesn't matter
 
1399
  much which value is used for the viewer type.  This "feature" of the
 
1400
  viewer is included just to be compatible with the old SGI Inventor
 
1401
  API.
 
1402
*/
 
1403
/*!
 
1404
  \var So@Gui@Viewer::Type So@Gui@Viewer::BROWSER
 
1405
 
 
1406
  If a user-supplied scenegraph passed into the setSceneGraph()
 
1407
  function does not contain a camera, setting the viewer type to
 
1408
  BROWSER will make the viewer in that case automatically set up a
 
1409
  camera outside the scene, as part of the viewer's private and hidden
 
1410
  "supergraph".
 
1411
*/
 
1412
/*!
 
1413
  \var So@Gui@Viewer::Type So@Gui@Viewer::EDITOR
 
1414
 
 
1415
  If a user-supplied scenegraph passed into the setSceneGraph)
 
1416
  function does not contain a camera, setting the viewer type to
 
1417
  EDITOR will make the viewer in that case automatically set up a
 
1418
  camera \e in the user-supplied scene.
 
1419
 
 
1420
  So if you want to avoid having the So@Gui@Viewer class muck about
 
1421
  with your supplied scenegraph, set the type-flag to
 
1422
  So@Gui@Viewer::BROWSER instead.
 
1423
*/
 
1424
 
 
1425
/*!
 
1426
  \enum So@Gui@Viewer::DrawType
 
1427
 
 
1428
  Contains valid values for the first argument to the
 
1429
  So@Gui@Viewer::setDrawStyle() call. Decides the effect of the second
 
1430
  argument.
 
1431
 
 
1432
  \sa So@Gui@Viewer::setDrawStyle(), So@Gui@Viewer::DrawStyle
 
1433
*/
 
1434
/*!
 
1435
  \var So@Gui@Viewer::DrawType So@Gui@Viewer::STILL
 
1436
 
 
1437
  If this value is passed as the first argument of
 
1438
  So@Gui@Viewer::setDrawStyle(), the second argument decides which
 
1439
  draw style to use when the viewer camera is standing still in the
 
1440
  same position with the same orientation -- i.e. when the end user is
 
1441
  \e not interacting with the scene camera.
 
1442
*/
 
1443
/*!
 
1444
  \var So@Gui@Viewer::DrawType So@Gui@Viewer::INTERACTIVE
 
1445
 
 
1446
  If this value is passed as the first argument of
 
1447
  So@Gui@Viewer::setDrawStyle(), the second argument decides which
 
1448
  draw style to use when the end user is interacting with the scene
 
1449
  camera, causing continuous animation redraws.
 
1450
*/
 
1451
 
 
1452
/*!
 
1453
  \enum So@Gui@Viewer::DrawStyle
 
1454
 
 
1455
  Decides drawstyle for a scene with either a still camera or an
 
1456
  animating camera.
 
1457
 
 
1458
  \sa So@Gui@Viewer::setDrawStyle(), So@Gui@Viewer::DrawType
 
1459
*/
 
1460
/*!
 
1461
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_AS_IS
 
1462
 
 
1463
  Normal rendering, draws all scene geometry in it's original style.
 
1464
*/
 
1465
/*!
 
1466
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_HIDDEN_LINE
 
1467
 
 
1468
  Draw scene in "hidden line" mode: that is, as wireframe with no
 
1469
  "see-through".
 
1470
 
 
1471
  Note that this is actually an expensive way to render, as the scene
 
1472
  must be rendered twice to achieve the effect of hiding lines behind
 
1473
  the invisible geometry.
 
1474
*/
 
1475
/*!
 
1476
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY
 
1477
 
 
1478
  Render the scene as normal, but overlay a set of lines showing the
 
1479
  contours of all polygons.
 
1480
*/
 
1481
/*!
 
1482
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_NO_TEXTURE
 
1483
 
 
1484
  Render scene without textures.
 
1485
*/
 
1486
/*!
 
1487
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_COMPLEXITY
 
1488
 
 
1489
  Render all "complex" shape types with low complexity to improve
 
1490
  rendering performance.
 
1491
 
 
1492
  "Complex shapes" in this context includes spheres, cones, cylinder,
 
1493
  NURBS surfaces, and others which are tesselated to polygons before
 
1494
  being rendered.
 
1495
*/
 
1496
/*!
 
1497
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LINE
 
1498
 
 
1499
  View all polygon geometry in wireframe mode.
 
1500
*/
 
1501
/*!
 
1502
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_POINT
 
1503
 
 
1504
  Render only the vertex positions of the geometry.
 
1505
*/
 
1506
/*!
 
1507
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_BBOX
 
1508
 
 
1509
  View the scene's bounding boxes, instead of rendering the full
 
1510
  geometry.
 
1511
 
 
1512
  A very efficient way of optimizing rendering performance for scenes
 
1513
  with high primitive counts while moving the camera about is to set
 
1514
  this mode for the So@Gui@Viewer::INTERACTIVE DrawType.
 
1515
*/
 
1516
/*!
 
1517
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_RES_LINE
 
1518
 
 
1519
  Render as wireframe and don't bother with getting them rendered
 
1520
  correctly in depth.
 
1521
*/
 
1522
/*!
 
1523
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_RES_POINT
 
1524
 
 
1525
  Render as vertex points and don't bother with getting them rendered
 
1526
  correctly in depth.
 
1527
*/
 
1528
/*!
 
1529
  \var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_SAME_AS_STILL
 
1530
 
 
1531
  Always render a scene with an animating camera (ie
 
1532
  So@Gui@Viewer::INTERACTIVE DrawType) in the same manner as scene
 
1533
  with a still camera.
 
1534
*/
 
1535
 
 
1536
/*!
 
1537
  \enum So@Gui@Viewer::BufferType
 
1538
  Set of valid values for So@Gui@Viewer::setBufferingType().
 
1539
*/
 
1540
/*!
 
1541
  \var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_SINGLE
 
1542
  Change underlying OpenGL canvas to be single-buffered.
 
1543
*/
 
1544
/*!
 
1545
  \var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_DOUBLE
 
1546
  Change underlying OpenGL canvas to be double-buffered.
 
1547
*/
 
1548
/*!
 
1549
  \var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_INTERACTIVE
 
1550
 
 
1551
  Set up so animation rendering is done in a double-buffered OpenGL
 
1552
  canvas, but ordinary rendering happens directly in the front-buffer.
 
1553
 
 
1554
  This mode can be useful with absurdly large scenes, as the rendering
 
1555
  will \e visibly progress, and one will avoid having the end user
 
1556
  wonder why nothing is happening while the scene is rendered to the
 
1557
  back buffer in the default So@Gui@Viewer::BUFFER_DOUBLE mode.
 
1558
*/
 
1559
 
 
1560
// *************************************************************************
 
1561
 
 
1562
/*!
 
1563
  \internal
 
1564
 
 
1565
  Return the parent node in the scene graph of the given \a node.
 
1566
  NB: this is just a quick'n'dirty thing for often executed code,
 
1567
  and doesn't cover cases where nodes have multiple parents.
 
1568
*/
 
1569
 
 
1570
static SoGroup *
 
1571
get_parent_of_node(So@Gui@ViewerP * pimpl, SoNode * root, SoNode * node)
 
1572
{
 
1573
  SbBool oldsearch = SoBaseKit::isSearchingChildren();
 
1574
  SoBaseKit::setSearchingChildren(TRUE);
 
1575
 
 
1576
  assert(node && root && "get_parent_of_node() called with null argument");
 
1577
  if (pimpl == NULL) {
 
1578
    SoSearchAction search;
 
1579
    search.setSearchingAll(TRUE);
 
1580
    search.setNode(node);
 
1581
    search.apply(root);
 
1582
    assert(search.getPath() && "node not found in scenegraph");
 
1583
    SoNode * parent = ((SoFullPath *)search.getPath())->getNodeFromTail(1);
 
1584
    assert(parent && "couldn't find parent");
 
1585
    SoBaseKit::setSearchingChildren(oldsearch);
 
1586
    return (SoGroup *)parent;
 
1587
  } else {
 
1588
    pimpl->searchaction->reset();
 
1589
    pimpl->searchaction->setSearchingAll(TRUE);
 
1590
    pimpl->searchaction->setNode(node);
 
1591
    pimpl->searchaction->apply(root);
 
1592
    assert(pimpl->searchaction->getPath() && "node not found in scenegraph");
 
1593
    SoNode * parent =
 
1594
      ((SoFullPath *) pimpl->searchaction->getPath())->getNodeFromTail(1);
 
1595
    assert(parent && "couldn't find parent");
 
1596
    pimpl->searchaction->reset();
 
1597
    SoBaseKit::setSearchingChildren(oldsearch);
 
1598
    return (SoGroup *)parent;
 
1599
  }
 
1600
}
 
1601
 
 
1602
// *************************************************************************
 
1603
 
 
1604
/*!
 
1605
  Constructor. \a parent, \a name and \a embed are passed on to
 
1606
  So@Gui@RenderArea, so see the documentation for our parent
 
1607
  constructor for for more information on those.
 
1608
 
 
1609
  The \a t type setting hints about what context the viewer will be
 
1610
  used in.  Usually not very interesting for the application
 
1611
  programmer, but if you want to make sure the So@Gui@Viewer class
 
1612
  doesn't muck about with your supplied scenegraph, set the type-flag
 
1613
  to So@Gui@Viewer::BROWSER.  (This "feature" of the viewer is
 
1614
  included just to be compatible with the old SGI Inventor API.)
 
1615
 
 
1616
  The \a build flag decides whether or not to delay building the
 
1617
  widgets / window which is going to make up the components of the
 
1618
  viewer.
 
1619
*/
 
1620
So@Gui@Viewer::So@Gui@Viewer(@WIDGET@ parent,
 
1621
                             const char * name,
 
1622
                             SbBool embed,
 
1623
                             So@Gui@Viewer::Type t,
 
1624
                             SbBool build)
 
1625
  : inherited(parent, name, embed, TRUE, TRUE, FALSE)
 
1626
{
 
1627
  PRIVATE(this) = new So@Gui@ViewerP(this);
 
1628
 
 
1629
  // initialization of protected data
 
1630
  PRIVATE(this)->type = t;
 
1631
  PRIVATE(this)->viewingflag = TRUE;
 
1632
  PRIVATE(this)->camera = NULL;
 
1633
  PRIVATE(this)->scenegraph = NULL;
 
1634
 
 
1635
  // initialization of internal data
 
1636
  PRIVATE(this)->cursoron = TRUE;
 
1637
  PRIVATE(this)->localsetbuffertype = FALSE;
 
1638
 
 
1639
  PRIVATE(this)->cameratype = SoPerspectiveCamera::getClassTypeId();
 
1640
  PRIVATE(this)->deletecamera = FALSE;
 
1641
  PRIVATE(this)->buffertype = this->isDoubleBuffer() ? BUFFER_DOUBLE : BUFFER_SINGLE;
 
1642
 
 
1643
  PRIVATE(this)->interactionstartCallbacks = new SoCallbackList;
 
1644
  PRIVATE(this)->interactionendCallbacks = new SoCallbackList;
 
1645
  PRIVATE(this)->interactionnesting = 0;
 
1646
 
 
1647
  PRIVATE(this)->seekdistance = 50.0f;
 
1648
  PRIVATE(this)->seekdistanceabs = FALSE;
 
1649
  PRIVATE(this)->seektopoint = TRUE;
 
1650
  PRIVATE(this)->seekperiod = 2.0f;
 
1651
  PRIVATE(this)->inseekmode = FALSE;
 
1652
  PRIVATE(this)->seeksensor = new SoTimerSensor(So@Gui@ViewerP::seeksensorCB, this);
 
1653
 
 
1654
  PRIVATE(this)->sceneroot = PRIVATE(this)->createSuperScene();
 
1655
  PRIVATE(this)->sceneroot->ref();
 
1656
 
 
1657
  PRIVATE(this)->drawstyles[STILL] = VIEW_AS_IS;
 
1658
  PRIVATE(this)->drawstyles[INTERACTIVE] = VIEW_SAME_AS_STILL;
 
1659
 
 
1660
  this->addStartCallback(So@Gui@ViewerP::interactivestartCB);
 
1661
  this->addFinishCallback(So@Gui@ViewerP::interactiveendCB);
 
1662
 
 
1663
  PRIVATE(this)->adjustclipplanes = TRUE;
 
1664
  PRIVATE(this)->autoclipbboxaction = NULL;
 
1665
 
 
1666
  PRIVATE(this)->stereoviewing = FALSE;
 
1667
  PRIVATE(this)->stereooffset = 0.1f;
 
1668
 
 
1669
  PRIVATE(this)->wireframeoverlaycolor = SbColor(1.0f, 0.0f, 0.0f);
 
1670
 
 
1671
  if (build) {
 
1672
    this->setClassName("So@Gui@Viewer");
 
1673
    @WIDGET@ widget = this->buildWidget(this->getParentWidget());
 
1674
    this->setBaseWidget(widget);
 
1675
  }
 
1676
 
 
1677
  PRIVATE(this)->fpsRoot = PRIVATE(this)->createFPSSuperimposition();
 
1678
  if (PRIVATE(this)->fpsRoot) {
 
1679
    this->addSuperimposition(PRIVATE(this)->fpsRoot);
 
1680
    this->setSuperimpositionEnabled(PRIVATE(this)->fpsRoot, FALSE);
 
1681
  }
 
1682
  PRIVATE(this)->resetFrameCounter();
 
1683
}
 
1684
 
 
1685
// *************************************************************************
 
1686
 
 
1687
/*!
 
1688
  Destructor.
 
1689
*/
 
1690
 
 
1691
So@Gui@Viewer::~So@Gui@Viewer()
 
1692
{
 
1693
  delete PRIVATE(this)->autoclipbboxaction;
 
1694
 
 
1695
  delete PRIVATE(this)->interactionstartCallbacks;
 
1696
  delete PRIVATE(this)->interactionendCallbacks;
 
1697
 
 
1698
  delete PRIVATE(this)->seeksensor;
 
1699
 
 
1700
  if (PRIVATE(this)->scenegraph) this->setSceneGraph(NULL);
 
1701
  PRIVATE(this)->sceneroot->unref();
 
1702
  if (PRIVATE(this)->superimpositions != NULL) {
 
1703
    for (int i = PRIVATE(this)->superimpositions->getLength() - 1; i >= 0; i--) {
 
1704
      SoNode * node = (SoNode *) (*PRIVATE(this)->superimpositions)[i];
 
1705
      node->unref();
 
1706
    }
 
1707
  }
 
1708
  delete PRIVATE(this);
 
1709
}
 
1710
 
 
1711
// *************************************************************************
 
1712
 
 
1713
// Note: the following function documentation block will also be used
 
1714
// for all the miscellaneous viewer subclasses, so keep it general.
 
1715
/*!
 
1716
  Set the camera we want to view the scene with.
 
1717
 
 
1718
  The camera passed in as an argument to this method \e must be part
 
1719
  of the viewer's scenegraph.
 
1720
 
 
1721
  If the application code doesn't explicitly set up a camera through
 
1722
  this method, the viewer will automatically scan through the
 
1723
  scenegraph to find a camera to use. If no camera is available in the
 
1724
  scenegraph at all, it will set up it's own camera.
 
1725
 
 
1726
  \sa getCamera()
 
1727
*/
 
1728
void
 
1729
So@Gui@Viewer::setCamera(SoCamera * cam)
 
1730
{
 
1731
  if (PRIVATE(this)->camera) {
 
1732
    // If we made the camera, detach it. Otherwise just leave it in
 
1733
    // the graph.
 
1734
    if (PRIVATE(this)->deletecamera) {
 
1735
      SoGroup * cameraparent =
 
1736
        get_parent_of_node(PRIVATE(this), PRIVATE(this)->sceneroot, PRIVATE(this)->camera);
 
1737
      cameraparent->removeChild(PRIVATE(this)->camera);
 
1738
      PRIVATE(this)->deletecamera = FALSE;
 
1739
    }
 
1740
 
 
1741
    PRIVATE(this)->camera->unref();
 
1742
  }
 
1743
 
 
1744
  PRIVATE(this)->camera = cam;
 
1745
 
 
1746
  if (PRIVATE(this)->camera) {
 
1747
    PRIVATE(this)->camera->ref();
 
1748
    this->saveHomePosition();
 
1749
    PRIVATE(this)->cameratype = PRIVATE(this)->camera->getTypeId();
 
1750
  }
 
1751
}
 
1752
 
 
1753
// *************************************************************************
 
1754
 
 
1755
/*!
 
1756
  Returns the camera currently used by the viewer for the user's main
 
1757
  viewpoint.
 
1758
 
 
1759
  It \e is possible that this function returns \c NULL, for instance
 
1760
  if there's no scenegraph present in the viewer.  (This is mostly
 
1761
  meant as a note for developers extending the So@Gui@ library, as
 
1762
  application programmers usually controls if and when a viewer
 
1763
  contains a scenegraph, and therefore know in advance if this method
 
1764
  will return a valid camera pointer.)
 
1765
 
 
1766
  \sa setCamera()
 
1767
*/
 
1768
 
 
1769
SoCamera *
 
1770
So@Gui@Viewer::getCamera(void) const
 
1771
{
 
1772
  return PRIVATE(this)->camera;
 
1773
}
 
1774
 
 
1775
// *************************************************************************
 
1776
 
 
1777
/*!
 
1778
  When the viewer has to make its own camera as a result of the graph
 
1779
  passed to setSceneGraph() not containing any camera nodes, this call
 
1780
  can be made in advance to decide which type the camera will be of.
 
1781
 
 
1782
  Default is to use an SoPerspectiveCamera.
 
1783
 
 
1784
  If this method is called when there is a scene graph and a camera
 
1785
  already set up, it will delete the old camera and set up a camera
 
1786
  with the new type if the \a t type is different from that of the
 
1787
  current camera.
 
1788
 
 
1789
  \sa getCameraType()
 
1790
*/
 
1791
 
 
1792
void
 
1793
So@Gui@Viewer::setCameraType(SoType t)
 
1794
{
 
1795
  SoType perspectivetype = SoPerspectiveCamera::getClassTypeId();
 
1796
  SoType orthotype = SoOrthographicCamera::getClassTypeId();
 
1797
  SbBool oldisperspective = PRIVATE(this)->cameratype.isDerivedFrom(perspectivetype);
 
1798
  SbBool newisperspective = t.isDerivedFrom(perspectivetype);
 
1799
 
 
1800
  if ((oldisperspective && newisperspective) ||
 
1801
      (!oldisperspective && !newisperspective)) // Same old, same old..
 
1802
    return;
 
1803
 
 
1804
  if (SO@GUI@_DEBUG) {
 
1805
    SbBool valid = TRUE;
 
1806
    if (t == SoType::badType()) valid = FALSE;
 
1807
    if (valid) {
 
1808
      valid = FALSE;
 
1809
      if (newisperspective) valid = TRUE;
 
1810
      if (t.isDerivedFrom(orthotype)) valid = TRUE;
 
1811
    }
 
1812
 
 
1813
    if (!valid) {
 
1814
      SoDebugError::post("So@Gui@Viewer::setCameraType",
 
1815
                         "not a valid camera type: '%s'",
 
1816
                         t == SoType::badType() ?
 
1817
                         "badType" : t.getName().getString());
 
1818
      return;
 
1819
    }
 
1820
  }
 
1821
 
 
1822
  if (PRIVATE(this)->camera != NULL) {
 
1823
    SoCamera * newcamera = (SoCamera *)t.createInstance();
 
1824
 
 
1825
    // Transfer and convert values from one camera type to the other.
 
1826
    if (newisperspective) {
 
1827
      So@Gui@ViewerP::convertOrtho2Perspective((SoOrthographicCamera *)PRIVATE(this)->camera,
 
1828
                                               (SoPerspectiveCamera *)newcamera);
 
1829
    }
 
1830
    else {
 
1831
      So@Gui@ViewerP::convertPerspective2Ortho((SoPerspectiveCamera *)PRIVATE(this)->camera,
 
1832
                                               (SoOrthographicCamera *)newcamera);
 
1833
    }
 
1834
 
 
1835
    SoGroup * cameraparent =
 
1836
      get_parent_of_node(PRIVATE(this), PRIVATE(this)->sceneroot, PRIVATE(this)->camera);
 
1837
    cameraparent->insertChild(newcamera,
 
1838
                              cameraparent->findChild(PRIVATE(this)->camera));
 
1839
    SoCamera * oldcamera = !PRIVATE(this)->deletecamera ? PRIVATE(this)->camera : NULL;
 
1840
 
 
1841
    // Store the current home position, as it will be implicitly reset
 
1842
    // by setCamera().
 
1843
    SoOrthographicCamera * homeo = new SoOrthographicCamera;
 
1844
    SoPerspectiveCamera * homep = new SoPerspectiveCamera;
 
1845
    homeo->ref();
 
1846
    homep->ref();
 
1847
    homeo->copyContents(PRIVATE(this)->storedortho, FALSE);
 
1848
    homep->copyContents(PRIVATE(this)->storedperspective, FALSE);
 
1849
 
 
1850
    this->setCamera(newcamera); // This will set PRIVATE(this)->cameratype.
 
1851
 
 
1852
    // Restore home position.
 
1853
    PRIVATE(this)->storedortho->copyContents(homeo, FALSE);
 
1854
    PRIVATE(this)->storedperspective->copyContents(homep, FALSE);
 
1855
    homeo->unref();
 
1856
    homep->unref();
 
1857
 
 
1858
    PRIVATE(this)->deletecamera = TRUE;
 
1859
    if (oldcamera) { cameraparent->removeChild(oldcamera); }
 
1860
  }
 
1861
  else { // A camera has not been instantiated for the scene.
 
1862
    PRIVATE(this)->cameratype = t; // No call to setCamera(), so set type explicitly.
 
1863
  }
 
1864
}
 
1865
 
 
1866
// *************************************************************************
 
1867
 
 
1868
/*!
 
1869
  Returns camera type which will be used when the viewer has to make its
 
1870
  own camera.
 
1871
 
 
1872
  Note that this call does \e not return the current cameratype, as one
 
1873
  might expect. Use getCamera() and SoType::getTypeId() for that inquiry.
 
1874
 
 
1875
  \sa setCameraType()
 
1876
*/
 
1877
 
 
1878
SoType
 
1879
So@Gui@Viewer::getCameraType(void) const
 
1880
{
 
1881
  return PRIVATE(this)->cameratype;
 
1882
}
 
1883
 
 
1884
// *************************************************************************
 
1885
 
 
1886
/*!
 
1887
  Reposition the current camera so we can see the complete scene.
 
1888
*/
 
1889
 
 
1890
void
 
1891
So@Gui@Viewer::viewAll(void)
 
1892
{
 
1893
  if (PRIVATE(this)->camera && PRIVATE(this)->scenegraph)
 
1894
    PRIVATE(this)->camera->viewAll(PRIVATE(this)->scenegraph, this->getViewportRegion());
 
1895
}
 
1896
 
 
1897
// *************************************************************************
 
1898
 
 
1899
/*!
 
1900
  Store the current camera settings for later retrieval with
 
1901
  resetToHomePosition().
 
1902
 
 
1903
  \sa resetToHomePosition()
 
1904
*/
 
1905
 
 
1906
void
 
1907
So@Gui@Viewer::saveHomePosition(void)
 
1908
{
 
1909
  if (! PRIVATE(this)->camera) return; // probably a scene-less viewer
 
1910
 
 
1911
  // FIXME: the old code here used SoType::isDerivedFrom() to test for
 
1912
  // camera type. This caused a crash in SoFieldData::overlay(), line
 
1913
  // 235 since the types of the field containers were not equal when
 
1914
  // calling copyContents() (I use a camera which is derived from
 
1915
  // SoPerspectiveCamera in my application).
 
1916
  //
 
1917
  // We should probably not use copyContents() to copy the camera
 
1918
  // field values, but copy field by field. Also, we should probably
 
1919
  // use SoType::createInstance() to store a copy of the camera, not
 
1920
  // just assume it's either a perspective or an orthographic camera.
 
1921
  //   pederb, 2002-05-27
 
1922
 
 
1923
  SoType t = PRIVATE(this)->camera->getTypeId();
 
1924
  if (t == SoOrthographicCamera::getClassTypeId()) {
 
1925
    PRIVATE(this)->storedortho->copyContents(PRIVATE(this)->camera, FALSE);
 
1926
    So@Gui@ViewerP::convertOrtho2Perspective((SoOrthographicCamera *)PRIVATE(this)->camera,
 
1927
                                             PRIVATE(this)->storedperspective);
 
1928
  }
 
1929
  else if (t == SoPerspectiveCamera::getClassTypeId()) {
 
1930
    PRIVATE(this)->storedperspective->copyContents(PRIVATE(this)->camera, FALSE);
 
1931
    So@Gui@ViewerP::convertPerspective2Ortho((SoPerspectiveCamera *)PRIVATE(this)->camera,
 
1932
                                             PRIVATE(this)->storedortho);
 
1933
  }
 
1934
  else {
 
1935
#if SO@GUI@_DEBUG
 
1936
    SoDebugError::postWarning("So@Gui@Viewer::saveHomePosition",
 
1937
                              "Only SoPerspectiveCamera and SoOrthographicCamera is supported.");
 
1938
#endif // SO@GUI_DEBUG
 
1939
  }
 
1940
}
 
1941
 
 
1942
// *************************************************************************
 
1943
 
 
1944
/*!
 
1945
  Restore the saved camera settings.
 
1946
 
 
1947
  \sa saveHomePosition()
 
1948
*/
 
1949
 
 
1950
void
 
1951
So@Gui@Viewer::resetToHomePosition(void)
 
1952
{
 
1953
  if (! PRIVATE(this)->camera) return; // probably a scene-less viewer
 
1954
  
 
1955
  SoType t = PRIVATE(this)->camera->getTypeId();
 
1956
  if (t == SoOrthographicCamera::getClassTypeId()) {
 
1957
    PRIVATE(this)->camera->copyContents(PRIVATE(this)->storedortho, FALSE);
 
1958
  }                                                                 
 
1959
  else if (t == SoPerspectiveCamera::getClassTypeId()) {
 
1960
    PRIVATE(this)->camera->copyContents(PRIVATE(this)->storedperspective, FALSE);
 
1961
  }
 
1962
}
 
1963
 
 
1964
// *************************************************************************
 
1965
 
 
1966
/*!
 
1967
  Turn the camera headlight on or off.
 
1968
 
 
1969
  Default is to have a headlight turned on.
 
1970
 
 
1971
  \sa isHeadlight(), getHeadlight()
 
1972
*/
 
1973
 
 
1974
void
 
1975
So@Gui@Viewer::setHeadlight(SbBool on)
 
1976
{
 
1977
  PRIVATE(this)->headlight->on = on;
 
1978
}
 
1979
 
 
1980
// *************************************************************************
 
1981
 
 
1982
/*!
 
1983
  Returns status of the viewer headlight, whether it is on or off.
 
1984
 
 
1985
  \sa setHeadlight(), getHeadlight()
 
1986
*/
 
1987
 
 
1988
SbBool
 
1989
So@Gui@Viewer::isHeadlight(void) const
 
1990
{
 
1991
  return PRIVATE(this)->headlight->on.getValue();
 
1992
}
 
1993
 
 
1994
// *************************************************************************
 
1995
 
 
1996
/*!
 
1997
  Returns the a pointer to the directional light node which is the
 
1998
  viewer headlight.
 
1999
 
 
2000
  The fields of the node is available for user editing.
 
2001
 
 
2002
  \sa isHeadlight(), setHeadlight()
 
2003
*/
 
2004
 
 
2005
SoDirectionalLight *
 
2006
So@Gui@Viewer::getHeadlight(void) const
 
2007
{
 
2008
  return PRIVATE(this)->headlight;
 
2009
}
 
2010
 
 
2011
// *************************************************************************
 
2012
 
 
2013
/*!
 
2014
  Set up a drawing style. The \a type argument specifies if the given
 
2015
  \a style should be interpreted as the drawstyle during animation or
 
2016
  when the camera is static.
 
2017
 
 
2018
  Default values for the drawing style is to render the scene "as is"
 
2019
  in both still mode and while the camera is moving.
 
2020
 
 
2021
  See the documentation for the \a DrawType and \a DrawStyle for more
 
2022
  information.
 
2023
 
 
2024
  \sa getDrawStyle()
 
2025
*/
 
2026
void
 
2027
So@Gui@Viewer::setDrawStyle(So@Gui@Viewer::DrawType type,
 
2028
                            So@Gui@Viewer::DrawStyle style)
 
2029
{
 
2030
  if (SO@GUI@_DEBUG) {
 
2031
    if ((type != STILL) && (type != INTERACTIVE)) {
 
2032
      SoDebugError::postWarning("So@Gui@Viewer::setDrawStyle",
 
2033
                                "unknown drawstyle type setting 0x%x", type);
 
2034
      return;
 
2035
    }
 
2036
  }
 
2037
 
 
2038
  if (style == this->getDrawStyle(type)) {
 
2039
    if (SO@GUI@_DEBUG && 0) { // debug
 
2040
      SoDebugError::postWarning("So@Gui@Viewer::setDrawStyle",
 
2041
                                "drawstyle for type 0x%02x already 0x%02x",
 
2042
                                type, style);
 
2043
    }
 
2044
    return;
 
2045
  }
 
2046
 
 
2047
  PRIVATE(this)->drawstyles[type] = style;
 
2048
  PRIVATE(this)->changeDrawStyle(PRIVATE(this)->currentDrawStyle());
 
2049
}
 
2050
 
 
2051
// *************************************************************************
 
2052
 
 
2053
/*!
 
2054
  Return current drawstyles for the given type (\a STILL or
 
2055
  \a INTERACTIVE).
 
2056
 
 
2057
  \sa setDrawStyle()
 
2058
*/
 
2059
 
 
2060
So@Gui@Viewer::DrawStyle
 
2061
So@Gui@Viewer::getDrawStyle(const So@Gui@Viewer::DrawType type) const
 
2062
{
 
2063
  if (SO@GUI@_DEBUG) {
 
2064
    if ((type != STILL) && (type != INTERACTIVE)) {
 
2065
      SoDebugError::postWarning("So@Gui@Viewer::setDrawStyle",
 
2066
                                "unknown drawstyle type setting 0x%x", type);
 
2067
      return PRIVATE(this)->drawstyles[STILL];
 
2068
    }
 
2069
  }
 
2070
 
 
2071
  return PRIVATE(this)->drawstyles[type];
 
2072
}
 
2073
 
 
2074
// *************************************************************************
 
2075
 
 
2076
/*!
 
2077
  Set the viewer's buffer type. Available types are \c
 
2078
  So@Gui@Viewer::BUFFER_SINGLE, \c So@Gui@Viewer::BUFFER_DOUBLE and \c
 
2079
  So@Gui@Viewer::BUFFER_INTERACTIVE.
 
2080
 
 
2081
  (With a buffer type of \c So@Gui@Viewer::BUFFER_INTERACTIVE, the
 
2082
  viewer will render with doublebuffering during user interaction and
 
2083
  with single buffering otherwise.)
 
2084
 
 
2085
  Default is \c So@Gui@Viewer::BUFFER_DOUBLE.
 
2086
 
 
2087
  \sa getBufferingType()
 
2088
*/
 
2089
 
 
2090
void
 
2091
So@Gui@Viewer::setBufferingType(So@Gui@Viewer::BufferType type)
 
2092
{
 
2093
  if (type == PRIVATE(this)->buffertype) return;
 
2094
 
 
2095
  if (type != BUFFER_SINGLE &&
 
2096
      type != BUFFER_DOUBLE &&
 
2097
      type != BUFFER_INTERACTIVE) {
 
2098
    if (SO@GUI@_DEBUG) {
 
2099
      SoDebugError::postWarning("So@Gui@Viewer::setBufferingType",
 
2100
                                "unknown buffer type 0x%x", type);
 
2101
    }
 
2102
    return;
 
2103
  }
 
2104
 
 
2105
  PRIVATE(this)->buffertype = type;
 
2106
 
 
2107
  PRIVATE(this)->localsetbuffertype = TRUE;
 
2108
  inherited::setDoubleBuffer(type == BUFFER_DOUBLE);
 
2109
  PRIVATE(this)->localsetbuffertype = FALSE;
 
2110
}
 
2111
 
 
2112
// *************************************************************************
 
2113
 
 
2114
/*!
 
2115
  Return the viewer's buffer type.
 
2116
 
 
2117
  \sa setBufferingType()
 
2118
*/
 
2119
 
 
2120
So@Gui@Viewer::BufferType
 
2121
So@Gui@Viewer::getBufferingType(void) const
 
2122
{
 
2123
  return PRIVATE(this)->buffertype;
 
2124
}
 
2125
 
 
2126
// *************************************************************************
 
2127
 
 
2128
// Note: this documentation for setViewing() will also be used for all
 
2129
// the miscellaneous viewer subclasses, so keep it general.
 
2130
/*!
 
2131
  Set view mode.
 
2132
 
 
2133
  If the view mode is on, user events will be caught and used to
 
2134
  influence the camera position / orientation. If view mode is off,
 
2135
  all events in the viewer canvas (like for instance keypresses or
 
2136
  mouseclicks and -movements) will be passed along to the scene graph.
 
2137
 
 
2138
  Default is to have the view mode active.
 
2139
 
 
2140
  \sa isViewing()
 
2141
*/
 
2142
void
 
2143
So@Gui@Viewer::setViewing(SbBool enable)
 
2144
{
 
2145
  if (PRIVATE(this)->viewingflag == enable) {
 
2146
    if (SO@GUI@_DEBUG) {
 
2147
      SoDebugError::postWarning("So@Gui@Viewer::setViewing",
 
2148
                                "unnecessary called");
 
2149
    }
 
2150
    return;
 
2151
  }
 
2152
 
 
2153
  PRIVATE(this)->viewingflag = enable;
 
2154
 
 
2155
  // Turn off the selection indicators when we go back from picking
 
2156
  // mode into viewing mode.
 
2157
  if (PRIVATE(this)->viewingflag) {
 
2158
    SoGLRenderAction * action = this->getGLRenderAction();
 
2159
    if (action != NULL)
 
2160
      SoLocateHighlight::turnOffCurrentHighlight(action);
 
2161
  }
 
2162
}
 
2163
 
 
2164
// *************************************************************************
 
2165
 
 
2166
/*!
 
2167
  Return state of view mode.
 
2168
 
 
2169
  \c TRUE means that the mode of the viewer is set such that user
 
2170
  interaction with the mouse is used to modify the position and
 
2171
  orientation of the camera.
 
2172
 
 
2173
  \sa setViewing()
 
2174
*/
 
2175
SbBool
 
2176
So@Gui@Viewer::isViewing(void) const
 
2177
{
 
2178
  return PRIVATE(this)->viewingflag;
 
2179
}
 
2180
 
 
2181
// *************************************************************************
 
2182
 
 
2183
/*!
 
2184
  Set whether or not the mouse cursor representation should be visible
 
2185
  in the viewer canvas.
 
2186
 
 
2187
  Default value is on.
 
2188
 
 
2189
  \sa isCursorEnabled()
 
2190
*/
 
2191
 
 
2192
void
 
2193
So@Gui@Viewer::setCursorEnabled(SbBool on)
 
2194
{
 
2195
  PRIVATE(this)->cursoron = on;
 
2196
}
 
2197
 
 
2198
// *************************************************************************
 
2199
 
 
2200
/*!
 
2201
  Returns visibility status of mouse cursor.
 
2202
 
 
2203
  \sa setCursorEnabled()
 
2204
*/
 
2205
 
 
2206
SbBool
 
2207
So@Gui@Viewer::isCursorEnabled(void) const
 
2208
{
 
2209
  return PRIVATE(this)->cursoron;
 
2210
}
 
2211
 
 
2212
// *************************************************************************
 
2213
 
 
2214
/*!
 
2215
  Turn on or off continuous automatic adjustments of the near and far
 
2216
  clipping planes.
 
2217
 
 
2218
  If on, the distance from the camera position to the near and far
 
2219
  planes will be calculated to be a "best fit" around the geometry in
 
2220
  the scene, to maximize the "stretch" of values for the visible
 
2221
  geometry in the z-buffer. This is important, as z-buffer resolution
 
2222
  is usually limited enough that one will quickly see flickering in
 
2223
  the rasterization of close polygons upon lousy utilization of the
 
2224
  z-buffer.
 
2225
 
 
2226
  Automatic clipping is on as default.
 
2227
 
 
2228
  For better control over what happens in boundary conditions (for
 
2229
  instance when the distance between near and far planes get very far,
 
2230
  or if geometry gets very close to the camera position), it is
 
2231
  possible to use the So@Gui@Viewer::setAutoClippingStrategy() method
 
2232
  to fine-tune the near/far clipping plane settings.
 
2233
 
 
2234
  \sa getAutoClipping()
 
2235
*/
 
2236
 
 
2237
void
 
2238
So@Gui@Viewer::setAutoClipping(SbBool enable)
 
2239
{
 
2240
  if (SO@GUI@_DEBUG) {
 
2241
    if (PRIVATE(this)->adjustclipplanes == enable) {
 
2242
      SoDebugError::postWarning("So@Gui@Viewer::setAutoClipping",
 
2243
                                "unnecessary called");
 
2244
      return;
 
2245
    }
 
2246
  }
 
2247
 
 
2248
  PRIVATE(this)->adjustclipplanes = enable;
 
2249
  if (enable) { this->scheduleRedraw(); }
 
2250
}
 
2251
 
 
2252
/*!
 
2253
  Set the auto clipping strategy used for auto clipping.
 
2254
 
 
2255
  When auto clipping is enabled, the near plane distance is calculated
 
2256
  so that it is just in front of the scene bounding box. If this near
 
2257
  plane is behind or very close to the projection point, one of the
 
2258
  following strategies will be used to calculate the new clipping
 
2259
  plane.
 
2260
 
 
2261
  The VARIABLE_NEAR_PLANE strategy considers the number of z buffer
 
2262
  bits available for the current OpenGL context, and uses \a value to
 
2263
  calculate the number of bits that is lost because of the far/near
 
2264
  ratio. \a value should be in the range [0.0, 1.0]. A higher \a value
 
2265
  will increase the z-buffer precision, but also push the near plane
 
2266
  further away from the projection point.
 
2267
  
 
2268
  The CONSTANT_NEAR_PLANE strategy simply sets the near plane to 
 
2269
  \a value. If \a value at some point approaches the far clipping
 
2270
  plane distance, the near plane distance will be set to far plane 
 
2271
  distance divided by 5000.0.
 
2272
 
 
2273
  The default strategy is VARIABLE_NEAR_PLANE.
 
2274
 
 
2275
 
 
2276
  It is also possible to register a callback method \a cb, which will
 
2277
  then be invoked after the near and far clipping planes has been
 
2278
  calculated by the So@Gui@Viewer code. The callback can then adjust
 
2279
  the values for the distance to the near and far planes to exactly
 
2280
  match the needs of the application (for instance at specific parts
 
2281
  in the scene), to limit the distance to either plane, or whatever
 
2282
  else needs to be controlled.
 
2283
 
 
2284
  The signature of the So@Gui@AutoClippingCB callback must match:
 
2285
  \code
 
2286
  SbVec2f myfunc(void * data, const SbVec2f & nearfar);
 
2287
  \endcode
 
2288
 
 
2289
  The first argument is the \a cbuserdata passed in along with the
 
2290
  callback function pointer itself (ie the callback function's
 
2291
  closure). The second argument is the near and far clipping plane
 
2292
  distances from the camera position, as calculated internally by the
 
2293
  viewer.
 
2294
 
 
2295
  The function callback can then modify the near and far clipping
 
2296
  plane distances to what will \e actually be used by the viewer.
 
2297
 
 
2298
  This is a good way of dynamically modifying the near and far
 
2299
  distances such that they at all times exactly matches the specific
 
2300
  layout of the application scene, for instance with regard to the
 
2301
  trade-off between z-buffer resolution and how early geometry is
 
2302
  clipped at the near plane (or at the far plane).
 
2303
 
 
2304
  Note that the internal near/far calculations should be good enough
 
2305
  for the vast majority of scenes. Application programmers should only
 
2306
  need to set up their own adjustments upon "unusual" scenes, like for
 
2307
  instance scenes with a large world space, but where one would still
 
2308
  like to be able to get up extremely close on details in some parts
 
2309
  of the scene.
 
2310
 
 
2311
 
 
2312
  \sa setAutoClipping() 
 
2313
*/
 
2314
void 
 
2315
So@Gui@Viewer::setAutoClippingStrategy(const AutoClippingStrategy strategy,
 
2316
                                       const float value,
 
2317
                                       So@Gui@AutoClippingCB * cb,
 
2318
                                       void * cbuserdata)
 
2319
{
 
2320
  PRIVATE(this)->autoclipstrategy = strategy;
 
2321
  PRIVATE(this)->autoclipvalue = value;
 
2322
  PRIVATE(this)->autoclipcb = cb;
 
2323
  PRIVATE(this)->autoclipuserdata = cbuserdata;
 
2324
 
 
2325
  if (PRIVATE(this)->autoclipstrategy == VARIABLE_NEAR_PLANE) {
 
2326
    // normalize the value so that the near plane isn't too near or
 
2327
    // too far from the projection point.  FIXME: calibrate this
 
2328
    // normalization, pederb, 2002-04-25
 
2329
    float v = So@Gui@Clamp(value, 0.0f, 1.0f); // just in case
 
2330
    v *= 0.8f;
 
2331
    v += 0.1f; // v will be in range [0.1, 0.9]
 
2332
 
 
2333
    PRIVATE(this)->autoclipvalue = v;
 
2334
  }
 
2335
  if (PRIVATE(this)->adjustclipplanes) {
 
2336
    this->scheduleRedraw();
 
2337
  }
 
2338
}
 
2339
 
 
2340
// *************************************************************************
 
2341
 
 
2342
/*!
 
2343
  Return value of the automatic near/far clipplane adjustment indicator.
 
2344
 
 
2345
  \sa setAutoClipping()
 
2346
*/
 
2347
 
 
2348
SbBool
 
2349
So@Gui@Viewer::isAutoClipping(
 
2350
  void) const
 
2351
{
 
2352
  return PRIVATE(this)->adjustclipplanes;
 
2353
}
 
2354
 
 
2355
// *************************************************************************
 
2356
 
 
2357
/*!
 
2358
  Turn stereo viewing on or off.
 
2359
 
 
2360
  Coin does "correct" stereo rendering, using the method known as
 
2361
  "parallel axis asymmetric frustum perspective projection". For more
 
2362
  information, see this link:
 
2363
 
 
2364
    http://astronomy.swin.edu.au/~pbourke/opengl/stereogl/
 
2365
 
 
2366
  \sa isStereoViewing()
 
2367
*/
 
2368
 
 
2369
void
 
2370
So@Gui@Viewer::setStereoViewing(// virtual
 
2371
  SbBool enable)
 
2372
{
 
2373
  PRIVATE(this)->stereoviewing = enable;
 
2374
  this->scheduleRedraw();
 
2375
}
 
2376
 
 
2377
// *************************************************************************
 
2378
 
 
2379
/*!
 
2380
  Returns a boolean indicating whether or not we're in stereo viewing
 
2381
  mode.
 
2382
 
 
2383
  NOTE: in the original InventorXt API, this method was virtual.  It is not
 
2384
  virtual here.
 
2385
 
 
2386
  \sa setStereoViewing()
 
2387
*/
 
2388
 
 
2389
SbBool
 
2390
So@Gui@Viewer::isStereoViewing(
 
2391
  void) const
 
2392
{
 
2393
  return PRIVATE(this)->stereoviewing;
 
2394
}
 
2395
 
 
2396
// *************************************************************************
 
2397
 
 
2398
/*!
 
2399
  Set the offset between the two viewpoints when in stereo mode.
 
2400
 
 
2401
  NOTE: In the original InventorXt API, this method was not virtual.
 
2402
 
 
2403
  \sa getStereoOffset()
 
2404
*/
 
2405
 
 
2406
void
 
2407
So@Gui@Viewer::setStereoOffset(// virtual
 
2408
  const float dist)
 
2409
{
 
2410
  PRIVATE(this)->stereooffset = dist;
 
2411
  this->scheduleRedraw();
 
2412
}
 
2413
 
 
2414
/*!
 
2415
  Return the offset distance between the two viewpoints when in stereo mode.
 
2416
 
 
2417
  \sa setStereoOffset()
 
2418
*/
 
2419
 
 
2420
float
 
2421
So@Gui@Viewer::getStereoOffset(
 
2422
  void) const
 
2423
{
 
2424
  return PRIVATE(this)->stereooffset;
 
2425
}
 
2426
 
 
2427
// *************************************************************************
 
2428
 
 
2429
/*!
 
2430
  Toggle between seeking to a point or seeking to an object.
 
2431
 
 
2432
  Default is to seek to a point.
 
2433
 
 
2434
  \sa isDetailSeek()
 
2435
*/
 
2436
 
 
2437
void
 
2438
So@Gui@Viewer::setDetailSeek(const SbBool on)
 
2439
{
 
2440
  if (SO@GUI@_DEBUG) {
 
2441
    if (PRIVATE(this)->seektopoint == on) {
 
2442
      SoDebugError::postWarning("So@Gui@Viewer::setDetailSeek",
 
2443
                                "unnecessary called");
 
2444
      return;
 
2445
    }
 
2446
  }
 
2447
 
 
2448
  PRIVATE(this)->seektopoint = on;
 
2449
}
 
2450
 
 
2451
// *************************************************************************
 
2452
 
 
2453
/*!
 
2454
  Returns a value indicating whether or not seeks will be performed
 
2455
  to the exact point of picking or just towards the picked object.
 
2456
 
 
2457
  \sa setDetailSeek()
 
2458
*/
 
2459
 
 
2460
SbBool
 
2461
So@Gui@Viewer::isDetailSeek(void) const
 
2462
{
 
2463
  return PRIVATE(this)->seektopoint;
 
2464
}
 
2465
 
 
2466
// *************************************************************************
 
2467
 
 
2468
/*!
 
2469
  Set the duration of animating the camera repositioning
 
2470
  after a successful seek. Call with \a seconds equal to \a 0.0 to make
 
2471
  the camera jump immediately to the correct spot.
 
2472
 
 
2473
  Default value is 2 seconds.
 
2474
 
 
2475
  \sa getSeekTime()
 
2476
*/
 
2477
 
 
2478
void
 
2479
So@Gui@Viewer::setSeekTime(const float seconds)
 
2480
{
 
2481
  if (seconds < 0.0f) {
 
2482
    if (SO@GUI@_DEBUG) {
 
2483
      SoDebugError::postWarning("So@Gui@Viewer::setSeekTime",
 
2484
                                "an attempt was made to set a negative seek "
 
2485
                                "time duration");
 
2486
    }
 
2487
    return;
 
2488
  }
 
2489
  PRIVATE(this)->seekperiod = seconds;
 
2490
}
 
2491
 
 
2492
// *************************************************************************
 
2493
 
 
2494
/*!
 
2495
  Returns the camera repositioning duration following a seek action.
 
2496
 
 
2497
  \sa setSeekTime()
 
2498
*/
 
2499
 
 
2500
float
 
2501
So@Gui@Viewer::getSeekTime(void) const
 
2502
{
 
2503
  return PRIVATE(this)->seekperiod;
 
2504
}
 
2505
 
 
2506
// *************************************************************************
 
2507
 
 
2508
/*!
 
2509
  Add a function to call when user interaction with the scene starts.
 
2510
 
 
2511
  \sa removeStartCallback(), addFinishCallback()
 
2512
*/
 
2513
 
 
2514
void
 
2515
So@Gui@Viewer::addStartCallback(So@Gui@ViewerCB * func, void * data)
 
2516
{
 
2517
  PRIVATE(this)->interactionstartCallbacks->addCallback((SoCallbackListCB *)func, data);
 
2518
}
 
2519
 
 
2520
/*!
 
2521
  Remove one of the functions which has been set up to be called when user
 
2522
  interaction with the scene starts.
 
2523
 
 
2524
  \sa addStartCallback(), removeFinishCallback()
 
2525
*/
 
2526
 
 
2527
void
 
2528
So@Gui@Viewer::removeStartCallback(So@Gui@ViewerCB * func, void * data)
 
2529
{
 
2530
  PRIVATE(this)->interactionstartCallbacks->removeCallback((SoCallbackListCB *)func,
 
2531
                                                  data);
 
2532
}
 
2533
 
 
2534
// *************************************************************************
 
2535
 
 
2536
/*!
 
2537
  Add a function to call when user interaction with the scene ends.
 
2538
 
 
2539
  \sa removeFinishCallback(), addStartCallback()
 
2540
*/
 
2541
 
 
2542
void
 
2543
So@Gui@Viewer::addFinishCallback(So@Gui@ViewerCB * func, void * data)
 
2544
{
 
2545
  PRIVATE(this)->interactionendCallbacks->addCallback((SoCallbackListCB *)func, data);
 
2546
}
 
2547
 
 
2548
/*!
 
2549
  Remove one of the functions which has been set up to be called when user
 
2550
  interaction with the scene ends.
 
2551
 
 
2552
  \sa addFinishCallback(), removeStartCallback()
 
2553
*/
 
2554
 
 
2555
void
 
2556
So@Gui@Viewer::removeFinishCallback(So@Gui@ViewerCB * func, void * data)
 
2557
{
 
2558
  PRIVATE(this)->interactionendCallbacks->removeCallback((SoCallbackListCB *)func,
 
2559
                                                data);
 
2560
}
 
2561
 
 
2562
// *************************************************************************
 
2563
 
 
2564
/*!
 
2565
  Set the color of the overlay wireframe to \a color.
 
2566
 
 
2567
  \sa getWireframeOverlayColor()
 
2568
*/
 
2569
void So@Gui@Viewer::setWireframeOverlayColor(const SbColor & color)
 
2570
{
 
2571
  PRIVATE(this)->wireframeoverlaycolor = color;
 
2572
  this->scheduleRedraw();
 
2573
}
 
2574
 
 
2575
// *************************************************************************
 
2576
 
 
2577
/*!
 
2578
  Returns the current color of the overlay wireframe. The default
 
2579
  color is [1,0,0], ie pure red.
 
2580
 
 
2581
  \sa setWireframeOverlayColor()
 
2582
*/
 
2583
const SbColor &So@Gui@Viewer::getWireframeOverlayColor(void) const
 
2584
{
 
2585
  return PRIVATE(this)->wireframeoverlaycolor;
 
2586
}
 
2587
 
 
2588
// *************************************************************************
 
2589
 
 
2590
/*!
 
2591
  Overloaded to update the local bufferingtype variable.
 
2592
 
 
2593
  \sa setBufferingType(), getBufferingType()
 
2594
*/
 
2595
 
 
2596
void
 
2597
So@Gui@Viewer::setDoubleBuffer(const SbBool on)
 
2598
{
 
2599
  if (!PRIVATE(this)->localsetbuffertype)
 
2600
    PRIVATE(this)->buffertype = on ? BUFFER_DOUBLE : BUFFER_SINGLE;
 
2601
 
 
2602
  inherited::setDoubleBuffer(on);
 
2603
}
 
2604
 
 
2605
// *************************************************************************
 
2606
 
 
2607
/*!
 
2608
  Give the viewer a scenegraph to render and interact with. Overloaded
 
2609
  from parent class so the viewer can add it's own nodes to control
 
2610
  rendering in different styles, rendering with a headlight, etc.
 
2611
 
 
2612
  The \a root node will be inserted under the \e viewer's root node,
 
2613
  which also covers the nodes necessary to implement the different
 
2614
  preferences drawing style settings.
 
2615
 
 
2616
  If no camera is part of the scene graph under \a root, one will be
 
2617
  added automatically.
 
2618
 
 
2619
  \sa getSceneGraph(), setCameraType()
 
2620
*/
 
2621
 
 
2622
void
 
2623
So@Gui@Viewer::setSceneGraph(SoNode * root)
 
2624
{
 
2625
  if ((root != NULL) && (root == PRIVATE(this)->scenegraph)) {
 
2626
    if (SO@GUI@_DEBUG) {
 
2627
      SoDebugError::postWarning("So@Gui@Viewer::setSceneGraph",
 
2628
                                "called with the same root as already set");
 
2629
    }
 
2630
    return;
 
2631
  }
 
2632
 
 
2633
  // If the So@Gui@RenderArea hasn't yet set up its pointer to the
 
2634
  // So@Gui@Viewer "viewer root" (i.e. the viewer-generated root above
 
2635
  // the user-supplied root), do that first.
 
2636
  if (!inherited::getSceneGraph())
 
2637
    inherited::setSceneGraph(PRIVATE(this)->sceneroot);
 
2638
 
 
2639
  if (PRIVATE(this)->scenegraph) {
 
2640
    if (this->getCamera())
 
2641
      this->setCamera(NULL);
 
2642
    // Release the old user-supplied graph.
 
2643
    PRIVATE(this)->usersceneroot->removeChild(PRIVATE(this)->scenegraph);
 
2644
    // old: PRIVATE(this)->sceneroot->removeChild(PRIVATE(this)->scenegraph);
 
2645
  }
 
2646
 
 
2647
  PRIVATE(this)->scenegraph = root;
 
2648
  if (!root) return;
 
2649
 
 
2650
  PRIVATE(this)->usersceneroot->addChild(PRIVATE(this)->scenegraph);
 
2651
  // old: PRIVATE(this)->sceneroot->addChild(PRIVATE(this)->scenegraph);
 
2652
 
 
2653
  // Search for a camera in the user-supplied scenegraph.
 
2654
  // SoSearchAction search;
 
2655
  // search.setType(SoCamera::getClassTypeId());
 
2656
  // search.apply(PRIVATE(this)->scenegraph);
 
2657
  SbBool oldsearch = SoBaseKit::isSearchingChildren();
 
2658
  SoBaseKit::setSearchingChildren(TRUE);
 
2659
 
 
2660
  PRIVATE(this)->searchaction->reset();
 
2661
  PRIVATE(this)->searchaction->setType(SoCamera::getClassTypeId());
 
2662
  PRIVATE(this)->searchaction->apply(PRIVATE(this)->scenegraph);
 
2663
 
 
2664
  SoBaseKit::setSearchingChildren(oldsearch);
 
2665
 
 
2666
  SoCamera * scenecamera = NULL;
 
2667
  if (PRIVATE(this)->searchaction->getPath() != NULL) {
 
2668
    SoFullPath * fullpath =
 
2669
      (SoFullPath *) PRIVATE(this)->searchaction->getPath();
 
2670
    scenecamera = (SoCamera *)fullpath->getTail();
 
2671
  }
 
2672
 
 
2673
#if 0 // debug
 
2674
  SoDebugError::postInfo("So@Gui@Viewer::setSceneGraph",
 
2675
                         "camera %sfound in graph",
 
2676
                         scenecamera ? "" : "not ");
 
2677
#endif // debug
 
2678
 
 
2679
  // Make our own camera if none was available.
 
2680
  if (!scenecamera) {
 
2681
    scenecamera = (SoCamera *) PRIVATE(this)->cameratype.createInstance();
 
2682
    PRIVATE(this)->deletecamera = TRUE;
 
2683
 
 
2684
    // If type==BROWSER, camera should be inserted in the private
 
2685
    // viewer "supergraph", if it's equal to EDITOR it should be
 
2686
    // inserted in the user-supplied scenegraph.
 
2687
    if (PRIVATE(this)->type == So@Gui@Viewer::BROWSER) {
 
2688
      PRIVATE(this)->sceneroot->insertChild(scenecamera, 1);
 
2689
    }
 
2690
    else { // PRIVATE(this)->type == So@Gui@Viewer::EDITOR
 
2691
      if (PRIVATE(this)->scenegraph->isOfType(SoGroup::getClassTypeId())) {
 
2692
        // At the uppermost leftmost position in the user-supplied
 
2693
        // scenegraph.
 
2694
        ((SoGroup *)PRIVATE(this)->scenegraph)->insertChild(scenecamera, 0);
 
2695
      }
 
2696
      else {
 
2697
        // Make an extra depth level to fit the camera node into the
 
2698
        // user-scenegraph.
 
2699
        SoGroup * g = new SoGroup;
 
2700
        g->addChild(scenecamera);
 
2701
        g->addChild(PRIVATE(this)->scenegraph);
 
2702
        PRIVATE(this)->usersceneroot->removeChild(PRIVATE(this)->scenegraph);
 
2703
        PRIVATE(this)->usersceneroot->addChild(g);
 
2704
        PRIVATE(this)->scenegraph = g;
 
2705
      }
 
2706
    }
 
2707
 
 
2708
    scenecamera->viewAll(PRIVATE(this)->scenegraph, this->getViewportRegion());
 
2709
  }
 
2710
 
 
2711
  this->setCamera(scenecamera);
 
2712
}
 
2713
 
 
2714
// *************************************************************************
 
2715
 
 
2716
// doc in super
 
2717
SoNode *
 
2718
So@Gui@Viewer::getSceneGraph(void)
 
2719
{
 
2720
  // Overloaded from parent class to return the root of the scene
 
2721
  // graph set by the user, without the extras added by the viewer to
 
2722
  // control rendering.
 
2723
  return PRIVATE(this)->scenegraph;
 
2724
}
 
2725
 
 
2726
// *************************************************************************
 
2727
 
 
2728
// Note: the following function documentation block will also be used
 
2729
// for all the miscellaneous viewer subclasses, so keep it general.
 
2730
/*!
 
2731
  Put the viewer in or out of "waiting-to-seek" mode.
 
2732
 
 
2733
  If the user performs a mouse button click when the viewer is in
 
2734
  "waiting-to-seek" mode, the camera will be repositioned so the
 
2735
  camera focal point lies on the point of the geometry under the mouse
 
2736
  cursor.
 
2737
 
 
2738
  \sa isSeekMode(), setDetailSeek()
 
2739
*/
 
2740
void
 
2741
So@Gui@Viewer::setSeekMode(SbBool enable)
 
2742
{
 
2743
  if (SO@GUI@_DEBUG) {
 
2744
    // User might have switched mode during seek, so if enable==FALSE,
 
2745
    // isViewing() is irrelevant.
 
2746
    if (enable) { assert(this->isViewing()); }
 
2747
  }
 
2748
 
 
2749
  if (!enable && PRIVATE(this)->seeksensor->isScheduled()) {
 
2750
    PRIVATE(this)->seeksensor->unschedule();
 
2751
    this->interactiveCountDec();
 
2752
  }
 
2753
 
 
2754
  PRIVATE(this)->inseekmode = enable;
 
2755
}
 
2756
 
 
2757
// *************************************************************************
 
2758
 
 
2759
/*!
 
2760
  Return a flag which indicates whether or not the viewer is in
 
2761
  "waiting-to-seek" mode.
 
2762
 
 
2763
  (The actual animated translation will not occur until the end user
 
2764
  really \e starts the seek operation, typically by clicking with the
 
2765
  left mousebutton.)
 
2766
 
 
2767
  \sa setSeekMode()
 
2768
*/
 
2769
SbBool
 
2770
So@Gui@Viewer::isSeekMode(void) const
 
2771
{
 
2772
  return PRIVATE(this)->inseekmode;
 
2773
}
 
2774
 
 
2775
// *************************************************************************
 
2776
 
 
2777
/*!
 
2778
  Call this method to initiate a seek action towards the 3D
 
2779
  intersection of the scene and the ray from the screen coordinate's
 
2780
  point and in the same direction as the camera is pointing.
 
2781
 
 
2782
  Returns \c TRUE if the ray from the \a screenpos position intersect
 
2783
  with any parts of the onscreen geometry, otherwise \c FALSE.
 
2784
*/
 
2785
SbBool
 
2786
So@Gui@Viewer::seekToPoint(const SbVec2s screenpos)
 
2787
{
 
2788
  if (! PRIVATE(this)->camera)
 
2789
    return FALSE;
 
2790
 
 
2791
  SoRayPickAction rpaction(this->getViewportRegion());
 
2792
  rpaction.setPoint(screenpos);
 
2793
  rpaction.setRadius(2);
 
2794
  rpaction.apply(PRIVATE(this)->sceneroot);
 
2795
 
 
2796
  SoPickedPoint * picked = rpaction.getPickedPoint();
 
2797
  if (!picked) {
 
2798
    // FIXME: this inc seems bogus, but is needed now due to buggy
 
2799
    // code in for instance the examinerviewer
 
2800
    // processSoEvent(). 20020510 mortene.
 
2801
#if 1
 
2802
    this->interactiveCountInc(); // decremented in setSeekMode(FALSE)
 
2803
#endif // FIXME
 
2804
    this->setSeekMode(FALSE);
 
2805
    return FALSE;
 
2806
  }
 
2807
 
 
2808
  SbVec3f hitpoint;
 
2809
  if (PRIVATE(this)->seektopoint) {
 
2810
    hitpoint = picked->getPoint();
 
2811
  } 
 
2812
  else {
 
2813
    SoGetBoundingBoxAction bbaction(this->getViewportRegion());
 
2814
    bbaction.apply(picked->getPath());
 
2815
    SbBox3f bbox = bbaction.getBoundingBox();
 
2816
    hitpoint = bbox.getCenter();
 
2817
  }
 
2818
 
 
2819
  PRIVATE(this)->camerastartposition = PRIVATE(this)->camera->position.getValue();
 
2820
  PRIVATE(this)->camerastartorient = PRIVATE(this)->camera->orientation.getValue();
 
2821
 
 
2822
  // move point to the camera coordinate system, consider
 
2823
  // transformations before camera in the scene graph
 
2824
  SbMatrix cameramatrix, camerainverse;
 
2825
  PRIVATE(this)->getCameraCoordinateSystem(PRIVATE(this)->camera,
 
2826
                                           PRIVATE(this)->sceneroot,
 
2827
                                           cameramatrix,
 
2828
                                           camerainverse);
 
2829
  camerainverse.multVecMatrix(hitpoint, hitpoint);
 
2830
 
 
2831
  float fd = PRIVATE(this)->seekdistance;
 
2832
  if (!PRIVATE(this)->seekdistanceabs)
 
2833
    fd *= (hitpoint - PRIVATE(this)->camera->position.getValue()).length()/100.0f;
 
2834
  PRIVATE(this)->camera->focalDistance = fd;
 
2835
 
 
2836
  SbVec3f dir = hitpoint - PRIVATE(this)->camerastartposition;
 
2837
  dir.normalize();
 
2838
 
 
2839
  // find a rotation that rotates current camera direction into new
 
2840
  // camera direction.
 
2841
  SbVec3f olddir;
 
2842
  PRIVATE(this)->camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), olddir);
 
2843
  SbRotation diffrot(olddir, dir);
 
2844
  PRIVATE(this)->cameraendposition = hitpoint - fd * dir;
 
2845
  PRIVATE(this)->cameraendorient = PRIVATE(this)->camera->orientation.getValue() * diffrot;
 
2846
 
 
2847
  if (PRIVATE(this)->seeksensor->isScheduled()) {
 
2848
    PRIVATE(this)->seeksensor->unschedule();
 
2849
    this->interactiveCountDec();
 
2850
  }
 
2851
 
 
2852
  PRIVATE(this)->seeksensor->setBaseTime(SbTime::getTimeOfDay());
 
2853
  PRIVATE(this)->seeksensor->schedule();
 
2854
  this->interactiveCountInc();
 
2855
 
 
2856
  return TRUE;
 
2857
}
 
2858
 
 
2859
// Documented in superclass. Overridden from parent class to be able
 
2860
// to do the necessary two-pass rendering e.g. if the drawing style is
 
2861
// hidden line.
 
2862
void
 
2863
So@Gui@Viewer::actualRedraw(void)
 
2864
{
 
2865
  SbTime redrawtime = SbTime::getTimeOfDay();
 
2866
 
 
2867
  SbBool clearcol = this->isClearBeforeRender();
 
2868
 
 
2869
  if (this->isStereoViewing()) {
 
2870
    SbColor bgcol = this->getSceneManager()->getBackgroundColor();
 
2871
 
 
2872
    SbBool quadstereo = this->isQuadBufferStereo();
 
2873
#ifndef HAVE_SOCAMERA_SETSTEREOMODE
 
2874
    SoCamera * camera = this->getCamera();
 
2875
    SbVec3f camerapos = camera->position.getValue();
 
2876
    SbVec3f cameradir(0.0f, 0.0f, -1.0f);
 
2877
    SbVec3f offsetvec(1.0f, 0.0f, 0.0f);
 
2878
    float offset = this->getStereoOffset() * 0.5f;
 
2879
    SbRotation camerarot = camera->orientation.getValue();
 
2880
    camerarot.multVec(cameradir, cameradir);
 
2881
    camerarot.multVec(offsetvec, offsetvec);
 
2882
    SbVec3f focalpoint = camerapos + cameradir * camera->focalDistance.getValue();
 
2883
 
 
2884
    SbBool notifystore = camera->isNotifyEnabled();
 
2885
    SbBool positionstore = camera->position.isNotifyEnabled();
 
2886
    SbBool orientationstore = camera->orientation.isNotifyEnabled();
 
2887
    // turn off notification to avoid redraws
 
2888
    camera->enableNotify(FALSE);
 
2889
    camera->position.enableNotify(FALSE);
 
2890
    camera->orientation.enableNotify(FALSE);
 
2891
 
 
2892
    camera->position = camerapos - offsetvec * offset;
 
2893
    SbVec3f dir = focalpoint - camera->position.getValue();
 
2894
    SbRotation rot(cameradir, dir);
 
2895
    camera->orientation = camerarot * rot;
 
2896
    if (quadstereo) {
 
2897
      glDrawBuffer(this->isDoubleBuffer() ? GL_BACK_LEFT : GL_FRONT_LEFT);
 
2898
      PRIVATE(this)->reallyRedraw(clearcol);
 
2899
    }
 
2900
    else { // red/cyan
 
2901
      glDrawBuffer(this->isDoubleBuffer() ? GL_BACK : GL_FRONT);
 
2902
      glClearColor(bgcol[0], bgcol[1], bgcol[2], 0.0f);
 
2903
      glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
 
2904
      glColorMask(1,0,0,1); // draw red-only
 
2905
      PRIVATE(this)->reallyRedraw(FALSE, FALSE);
 
2906
    }
 
2907
 
 
2908
    // right eye
 
2909
    camera->position = camerapos + offsetvec * offset;
 
2910
    dir = focalpoint - camera->position.getValue();
 
2911
    rot.setValue(cameradir, dir);
 
2912
    camera->orientation = camerarot * rot;
 
2913
 
 
2914
    if (quadstereo) {
 
2915
      glDrawBuffer(this->isDoubleBuffer() ? GL_BACK_RIGHT : GL_FRONT_RIGHT);
 
2916
      PRIVATE(this)->reallyRedraw(clearcol);
 
2917
    }
 
2918
    else {
 
2919
      glColorMask(0,1,1,1); // draw green and blue
 
2920
      PRIVATE(this)->reallyRedraw(FALSE, TRUE);
 
2921
    }
 
2922
 
 
2923
    // restore camera values and enable notification
 
2924
    camera->position = camerapos;
 
2925
    camera->orientation = camerarot;
 
2926
    camera->position.enableNotify(positionstore);
 
2927
    camera->orientation.enableNotify(orientationstore);
 
2928
    camera->enableNotify(notifystore);
 
2929
    if (quadstereo) {
 
2930
      glDrawBuffer(this->isDoubleBuffer() ? GL_BACK : GL_FRONT);
 
2931
    }
 
2932
    else {
 
2933
      glColorMask(1,1,1,1); // restore GL color mask
 
2934
    }
 
2935
#else // HAVE_SOCAMERA_SETSTEREOMODE
 
2936
 
 
2937
    if (!quadstereo) {
 
2938
      SoCamera * camera = this->getCamera();
 
2939
      if (this->isDoubleBuffer())
 
2940
        glDrawBuffer(GL_BACK);
 
2941
      else
 
2942
        glDrawBuffer(GL_FRONT);
 
2943
      glClearColor(bgcol[0], bgcol[1], bgcol[2], 0.0f);
 
2944
      glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
 
2945
      glColorMask(1,0,0,1); // draw red-only
 
2946
      camera->setStereoAdjustment(this->getStereoOffset());
 
2947
      camera->setStereoMode(SoCamera::LEFT_VIEW);
 
2948
      PRIVATE(this)->reallyRedraw(FALSE, FALSE);
 
2949
 
 
2950
      camera->setStereoMode(SoCamera::RIGHT_VIEW);
 
2951
      glColorMask(0,1,1,1); // draw green and blue
 
2952
      PRIVATE(this)->reallyRedraw(FALSE, TRUE);
 
2953
      camera->setStereoMode(SoCamera::MONOSCOPIC);
 
2954
      glColorMask(1,1,1,1); // restore GL color mask
 
2955
    }
 
2956
    else {
 
2957
      SoCamera * camera = this->getCamera();
 
2958
      if (this->isDoubleBuffer())
 
2959
        glDrawBuffer(GL_BACK_LEFT);
 
2960
      else
 
2961
        glDrawBuffer(GL_FRONT_LEFT);
 
2962
      camera->setStereoAdjustment(this->getStereoOffset());
 
2963
      camera->setStereoMode(SoCamera::LEFT_VIEW);
 
2964
      PRIVATE(this)->reallyRedraw(clearcol);
 
2965
      camera->setStereoMode(SoCamera::RIGHT_VIEW);
 
2966
      if (this->isDoubleBuffer())
 
2967
        glDrawBuffer(GL_BACK_RIGHT);
 
2968
      else
 
2969
        glDrawBuffer(GL_FRONT_RIGHT);
 
2970
      PRIVATE(this)->reallyRedraw(clearcol);
 
2971
      camera->setStereoMode(SoCamera::MONOSCOPIC);
 
2972
      if (this->isDoubleBuffer())
 
2973
        glDrawBuffer(GL_BACK);
 
2974
      else
 
2975
        glDrawBuffer(GL_FRONT);
 
2976
    }
 
2977
#endif // HAVE_SOCAMERA_SETSTEREOMODE
 
2978
  }
 
2979
  else PRIVATE(this)->reallyRedraw(clearcol);
 
2980
 
 
2981
  if (PRIVATE(this)->superimpositions != NULL) {
 
2982
    SoGLRenderAction * raaction = this->getSceneManager()->getGLRenderAction();
 
2983
    SbBool first = TRUE;
 
2984
    SbBool zWasEnabled = FALSE;
 
2985
    for (int i = 0; i < PRIVATE(this)->superimpositions->getLength(); i++) {
 
2986
      if (PRIVATE(this)->superimpositionsenabled[i] != FALSE) {
 
2987
        if (first) {
 
2988
          // save Z buffer state and disable
 
2989
          zWasEnabled = glIsEnabled(GL_DEPTH_TEST) ? TRUE : FALSE;
 
2990
          glDisable(GL_DEPTH_TEST);
 
2991
          first = FALSE;
 
2992
        }
 
2993
        SoNode * scene = (SoNode *) (*PRIVATE(this)->superimpositions)[i];
 
2994
        raaction->apply(scene);
 
2995
      }
 
2996
    }
 
2997
    if (!first && zWasEnabled) glEnable(GL_DEPTH_TEST);
 
2998
  }
 
2999
 
 
3000
  redrawtime = SbTime::getTimeOfDay() - redrawtime;
 
3001
  PRIVATE(this)->recordFPS(redrawtime.getValue());
 
3002
}
 
3003
 
 
3004
// *************************************************************************
 
3005
 
 
3006
/*!
 
3007
  To be able to trigger callback functions when user interaction starts
 
3008
  and/or stops, we need to keep track of the viewer state (i.e. are we in
 
3009
  still mode or in animation mode?).
 
3010
 
 
3011
  So@Gui@Viewer automatically adds callbacks to switch between still and
 
3012
  moving draw style, and to switch between single/double buffer when
 
3013
  the buffer type is \a INTERACTIVE.
 
3014
 
 
3015
  \sa interactiveCountDec(), getInteractiveCount()
 
3016
  \sa addStartCallback(), addFinishCallback()
 
3017
  \sa removeStartCallback(), removeFinishCallback()
 
3018
  \sa setDrawStyle(), setBufferingType()
 
3019
*/
 
3020
 
 
3021
void
 
3022
So@Gui@Viewer::interactiveCountInc(void)
 
3023
{
 
3024
  // Catch problems with missing interactiveCountDec() calls.
 
3025
  assert(PRIVATE(this)->interactionnesting < 100);
 
3026
 
 
3027
  if (++(PRIVATE(this)->interactionnesting) == 1) {
 
3028
    PRIVATE(this)->interactionstartCallbacks->invokeCallbacks(this);
 
3029
    PRIVATE(this)->resetFrameCounter();
 
3030
  }
 
3031
 
 
3032
#if 0 // debug
 
3033
  SoDebugError::postInfo("So@Gui@Viewer::interactiveCountInc", "%d -> %d",
 
3034
                         PRIVATE(this)->interactionnesting - 1,
 
3035
                         PRIVATE(this)->interactionnesting);
 
3036
#endif // debug
 
3037
}
 
3038
 
 
3039
// *************************************************************************
 
3040
 
 
3041
/*!
 
3042
  To be able to trigger callback functions when user interaction starts
 
3043
  and/or stops, we need to keep track of the viewer state (i.e. are we in
 
3044
  still mode or in animation mode?).
 
3045
 
 
3046
  So@Gui@Viewer automatically adds callbacks to switch between still and
 
3047
  moving draw style, and to switch between single/double buffer when
 
3048
  the buffer type is \a INTERACTIVE.
 
3049
 
 
3050
  \sa interactiveCountInc(), getInteractiveCount()
 
3051
  \sa addStartCallback(), addFinishCallback()
 
3052
  \sa removeStartCallback(), removeFinishCallback()
 
3053
  \sa setDrawStyle(), setBufferingType()
 
3054
*/
 
3055
 
 
3056
void
 
3057
So@Gui@Viewer::interactiveCountDec(void)
 
3058
{
 
3059
  // FIXME: The UI toolkits may cause the interactionnesting to go
 
3060
  // below zero by triggering press and release events in different
 
3061
  // widgets. mariusbu 20010709.
 
3062
 
 
3063
  // FIXME: just to clarify; this is due to programming mistakes on
 
3064
  // our behalf and should be cleaned up. We're using a simple
 
3065
  // work-around / ignore strategy for now, though, as getting this
 
3066
  // 100% correct is hard (there are so many possible ways of user
 
3067
  // interaction with a viewer canvas) and the end-user will usually
 
3068
  // not notice any problems at all. So that's why we are using a
 
3069
  // warning instead of an assert(). 20010815 mortene.
 
3070
 
 
3071
  // FIXME: here's one known way to trigger the bug: hit "s" in the
 
3072
  // examinerviewer in EXAMINE mode, then while seeking hit ESC to put
 
3073
  // the viewer in INTERACT mode. When the seek is completed, the
 
3074
  // count will become -1.  20010912 mortene.
 
3075
 
 
3076
  // FIXME: and another one (tested with SoXt): click and hold LMB in
 
3077
  // the canvas while in INTERACT mode, then hit 'Esc' to switch to
 
3078
  // EXAMINE mode, then release LMB. 20020325 mortene.
 
3079
 
 
3080
  if (SO@GUI@_DEBUG) {
 
3081
    if (PRIVATE(this)->interactionnesting <= 0) {
 
3082
      SoDebugError::postWarning("So@Gui@Viewer::interactiveCountDec",
 
3083
                                "interaction count nesting went below zero. "
 
3084
                                "This is due to an internal So@Gui@ bug.");
 
3085
    }
 
3086
  }
 
3087
 
 
3088
  if (--(PRIVATE(this)->interactionnesting) <= 0) {
 
3089
    PRIVATE(this)->interactionendCallbacks->invokeCallbacks(this);
 
3090
    PRIVATE(this)->interactionnesting = 0;
 
3091
  }
 
3092
}
 
3093
 
 
3094
// *************************************************************************
 
3095
 
 
3096
/*!
 
3097
  Return current interaction count nesting. If equal to zero, the viewer
 
3098
  is in animation mode, otherwise the camera is still.
 
3099
 
 
3100
  \sa interactiveCountInc(), interactiveCountDec()
 
3101
*/
 
3102
 
 
3103
int
 
3104
So@Gui@Viewer::getInteractiveCount(void) const
 
3105
{
 
3106
  return PRIVATE(this)->interactionnesting;
 
3107
}
 
3108
 
 
3109
// *************************************************************************
 
3110
 
 
3111
/*!
 
3112
  Set the value used for calculating how close the camera and intersection
 
3113
  hit point should be made at the end of a seek operation.
 
3114
 
 
3115
  The value can be interpreted as an absolute value in the given world
 
3116
  unit (which typically is meters) or as a percentage value of the
 
3117
  distance between the camera starting position and the intersection
 
3118
  hit point. This can be controlled through the
 
3119
  setSeekValueAsPercentage() method. It is as default used as an
 
3120
  absolute value.
 
3121
 
 
3122
  Default value is 50 (absolute distance or percent).
 
3123
 
 
3124
  \sa getSeekDistance(), setSeekValueAsPercentage(), setSeekTime()
 
3125
*/
 
3126
 
 
3127
void
 
3128
So@Gui@Viewer::setSeekDistance(const float distance)
 
3129
{
 
3130
  if (distance <= 0.0f) {
 
3131
    if (SO@GUI@_DEBUG) {
 
3132
      SoDebugError::postWarning("So@Gui@Viewer::setSeekDistance",
 
3133
                                "invalid seek distance value: %f",
 
3134
                                distance);
 
3135
    }
 
3136
    return;
 
3137
  }
 
3138
  PRIVATE(this)->seekdistance = distance;
 
3139
}
 
3140
 
 
3141
// *************************************************************************
 
3142
 
 
3143
/*!
 
3144
  Returns the current seek distance. Value given as an absolute scalar
 
3145
  length or as a percentage value of the original distance between
 
3146
  the hitpoint and the camera starting position.
 
3147
 
 
3148
  \sa setSeekDistance(), isSeekValueAsPercentage()
 
3149
*/
 
3150
 
 
3151
float
 
3152
So@Gui@Viewer::getSeekDistance(void) const
 
3153
{
 
3154
  return PRIVATE(this)->seekdistance;
 
3155
}
 
3156
 
 
3157
// *************************************************************************
 
3158
 
 
3159
/*!
 
3160
  Control whether or not the seek distance value should be interpreted as
 
3161
  a percentage value or as an absolute distance. See documentation on
 
3162
  setSeekDistance() for more information.
 
3163
 
 
3164
  \sa setSeekDistance(), isSeekValueAsPercentage()
 
3165
*/
 
3166
 
 
3167
void
 
3168
So@Gui@Viewer::setSeekValueAsPercentage(const SbBool on)
 
3169
{
 
3170
  if (SO@GUI@_DEBUG) {
 
3171
    if ((on && this->isSeekValuePercentage()) ||
 
3172
        (!on && !this->isSeekValuePercentage())) {
 
3173
      SoDebugError::postWarning("So@Gui@Viewer::setSeekDistanceAsPercentage",
 
3174
                                "unnecessary called, value already %s",
 
3175
                                on ? "on" : "off");
 
3176
      return;
 
3177
    }
 
3178
  }
 
3179
 
 
3180
  PRIVATE(this)->seekdistanceabs = on ? FALSE : TRUE;
 
3181
}
 
3182
 
 
3183
// *************************************************************************
 
3184
 
 
3185
/*!
 
3186
  Returns an boolean which indicates if the seek distance value from
 
3187
  getSeekDistance() should be interpreted as a percentage value or
 
3188
  as an absolute value.
 
3189
 
 
3190
  \sa setSeekValuePercentage(), getSeekDistance()
 
3191
*/
 
3192
 
 
3193
SbBool
 
3194
So@Gui@Viewer::isSeekValuePercentage(void) const
 
3195
{
 
3196
  return PRIVATE(this)->seekdistanceabs ? FALSE : TRUE;
 
3197
}
 
3198
 
 
3199
// ************************************************************************
 
3200
 
 
3201
/*!
 
3202
  This method is obsoleted in Coin So@Gui@.
 
3203
 */
 
3204
void
 
3205
So@Gui@Viewer::computeSeekFinalOrientation(void)
 
3206
{
 
3207
  SO@GUI@_OBSOLETED();
 
3208
}
 
3209
 
 
3210
// *************************************************************************
 
3211
 
 
3212
/*!
 
3213
  If the current camera is of perspective type, switch to
 
3214
  orthographic, and vice versa.
 
3215
 
 
3216
  Automatically calls So@Gui@Viewer::setCameraType() so the change
 
3217
  will immediately take place.
 
3218
*/
 
3219
void
 
3220
So@Gui@Viewer::toggleCameraType(void)
 
3221
{
 
3222
  SoType perspectivetype = SoPerspectiveCamera::getClassTypeId();
 
3223
  SoType orthotype = SoOrthographicCamera::getClassTypeId();
 
3224
 
 
3225
  this->setCameraType(PRIVATE(this)->cameratype.isDerivedFrom(perspectivetype)
 
3226
                       ? orthotype : perspectivetype);
 
3227
}
 
3228
 
 
3229
// ************************************************************************
 
3230
 
 
3231
/*!
 
3232
  Copies the settings of \a camera into our current camera.  Cameras
 
3233
  must be of the same class type.
 
3234
 */
 
3235
void
 
3236
So@Gui@Viewer::changeCameraValues(// virtual, protected
 
3237
  SoCamera * camera)
 
3238
{
 
3239
  assert(camera != NULL);
 
3240
 
 
3241
  SoCamera * cam = this->getCamera();
 
3242
  if (!cam) {
 
3243
    if (SO@GUI@_DEBUG) {
 
3244
      SoDebugError::postWarning("So@Gui@Viewer::changeCameraValues",
 
3245
                                "no current camera in the scenegraph");
 
3246
    }
 
3247
    return;
 
3248
  }
 
3249
  if (cam->getTypeId() != camera->getTypeId()) {
 
3250
    if (SO@GUI@_DEBUG) {
 
3251
      SoDebugError::postWarning("So@Gui@Viewer::changeCameraValues",
 
3252
                                "tried to copy data from camera of "
 
3253
                                "different type");
 
3254
    }
 
3255
    return;
 
3256
  }
 
3257
 
 
3258
  cam->copyFieldValues(camera, FALSE);
 
3259
}
 
3260
 
 
3261
// *************************************************************************
 
3262
 
 
3263
// doc in super
 
3264
void
 
3265
So@Gui@Viewer::sizeChanged(const SbVec2s & size)
 
3266
{
 
3267
  inherited::sizeChanged(size);
 
3268
}
 
3269
 
 
3270
// *************************************************************************
 
3271
 
 
3272
// Documented in superclass.
 
3273
SbBool
 
3274
So@Gui@Viewer::processSoEvent(const SoEvent * const event)
 
3275
{
 
3276
  const SoType type(event->getTypeId());
 
3277
  const SoKeyboardEvent * keyevent = NULL;
 
3278
 
 
3279
  if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
 
3280
    keyevent = (SoKeyboardEvent *) event;
 
3281
    // the ESC key switches between view and interact mode
 
3282
    if (keyevent->getKey() == SoKeyboardEvent::ESCAPE &&
 
3283
        keyevent->getState() == SoButtonEvent::DOWN) {
 
3284
      this->setViewing(this->isViewing() ? FALSE : TRUE);
 
3285
      return TRUE;
 
3286
    }
 
3287
  }
 
3288
 
 
3289
  // If not viewing, break off further handling and pass the event on
 
3290
  // to the So@Gui@RenderArea, which will pass it on to the
 
3291
  // scenegraph.
 
3292
  if (!this->isViewing()) { return inherited::processSoEvent(event); }
 
3293
 
 
3294
 
 
3295
  if (keyevent && (keyevent->getState() == SoButtonEvent::DOWN)) {
 
3296
    switch (keyevent->getKey()) {
 
3297
    case SoKeyboardEvent::S:
 
3298
      this->setSeekMode(this->isSeekMode() ? FALSE : TRUE);
 
3299
      return TRUE;
 
3300
    case SoKeyboardEvent::Q:
 
3301
      this->hide();
 
3302
      So@Gui@::exitMainLoop();
 
3303
      return TRUE;
 
3304
    case SoKeyboardEvent::HOME:
 
3305
      this->resetToHomePosition();
 
3306
      return TRUE;
 
3307
    case SoKeyboardEvent::LEFT_ARROW:
 
3308
      PRIVATE(this)->moveCameraScreen(SbVec2f(-0.1f, 0.0f));
 
3309
      return TRUE;
 
3310
    case SoKeyboardEvent::UP_ARROW:
 
3311
      PRIVATE(this)->moveCameraScreen(SbVec2f(0.0f, 0.1f));
 
3312
      return TRUE;
 
3313
    case SoKeyboardEvent::RIGHT_ARROW:
 
3314
      PRIVATE(this)->moveCameraScreen(SbVec2f(0.1f, 0.0f));
 
3315
      return TRUE;
 
3316
    case SoKeyboardEvent::DOWN_ARROW:
 
3317
      PRIVATE(this)->moveCameraScreen(SbVec2f(0.0f, -0.1f));
 
3318
      return TRUE;
 
3319
    default:
 
3320
      break;
 
3321
    }
 
3322
  }
 
3323
 
 
3324
  if (this->isSeekMode()) {
 
3325
    if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
 
3326
      SoMouseButtonEvent * const e = (SoMouseButtonEvent *) event;
 
3327
      if (e->getButton() == SoMouseButtonEvent::BUTTON1) {
 
3328
        if (e->getState() == SoButtonEvent::DOWN) {
 
3329
          this->seekToPoint(e->getPosition());
 
3330
        }
 
3331
        else {
 
3332
          // We got an LMB UP-event while in seek-mode, and we just
 
3333
          // swallow the event.
 
3334
        }
 
3335
        return TRUE;
 
3336
      }
 
3337
    }
 
3338
  }
 
3339
 
 
3340
  return FALSE;
 
3341
}
 
3342
 
 
3343
// *************************************************************************
 
3344
 
 
3345
/*!
 
3346
  This method is for setting up a superimposed scene graph on top
 
3347
  of the viewer scene graph.  It will be used for adding spin-rotation
 
3348
  coordinate systems, fly-viewer speed indicators and similar things.
 
3349
 
 
3350
  This method is not part of the original InventorXt API.
 
3351
*/
 
3352
 
 
3353
void
 
3354
So@Gui@Viewer::addSuperimposition(SoNode * scene)
 
3355
{
 
3356
  if (PRIVATE(this)->superimpositions == NULL) {
 
3357
    PRIVATE(this)->superimpositions = new SbPList;
 
3358
  }
 
3359
  PRIVATE(this)->searchaction->reset();
 
3360
  PRIVATE(this)->searchaction->setType(SoCamera::getClassTypeId());
 
3361
  PRIVATE(this)->searchaction->setInterest(SoSearchAction::FIRST);
 
3362
  PRIVATE(this)->searchaction->apply(scene);
 
3363
  if (PRIVATE(this)->searchaction->getPath() == NULL) {
 
3364
    // FIXME: set up default environment if there is no camera in the
 
3365
    // superimposition scene
 
3366
    if (SO@GUI@_DEBUG) {
 
3367
      SoDebugError::postInfo("So@Gui@Viewer::addSuperimposition",
 
3368
                             "cameraless superimpositions are not "
 
3369
                             "supported yet");
 
3370
    }
 
3371
    return;
 
3372
  }
 
3373
  scene->ref();
 
3374
  PRIVATE(this)->superimpositions->append(scene);
 
3375
  PRIVATE(this)->superimpositionsenabled.append(TRUE);
 
3376
}
 
3377
 
 
3378
// *************************************************************************
 
3379
 
 
3380
/*!
 
3381
  This method is not part of the original InventorXt API.
 
3382
*/
 
3383
 
 
3384
void
 
3385
So@Gui@Viewer::removeSuperimposition(SoNode * scene)
 
3386
{
 
3387
  assert(scene);
 
3388
  int idx = -1;
 
3389
  if (PRIVATE(this)->superimpositions == NULL) goto error;
 
3390
  idx = PRIVATE(this)->superimpositions->find(scene);
 
3391
  if (idx == -1) goto error;
 
3392
  assert(PRIVATE(this)->superimpositions != NULL);
 
3393
  PRIVATE(this)->superimpositions->remove(idx);
 
3394
  PRIVATE(this)->superimpositionsenabled.remove(idx);
 
3395
  scene->unref();
 
3396
  return;
 
3397
 
 
3398
 error:
 
3399
  if (SO@GUI@_DEBUG) {
 
3400
    SoDebugError::post("So@Gui@Viewer::removeSuperimposition",
 
3401
                       "no such superimposition");
 
3402
  }
 
3403
  return;
 
3404
}
 
3405
 
 
3406
// *************************************************************************
 
3407
 
 
3408
/*!
 
3409
  This method sets whether the superimposed scene graph should be traversed
 
3410
  or not.
 
3411
 
 
3412
  This method is not part of the original InventorXt API.
 
3413
*/
 
3414
 
 
3415
void
 
3416
So@Gui@Viewer::setSuperimpositionEnabled(SoNode * scene,
 
3417
                                         const SbBool enable)
 
3418
{
 
3419
  int idx = -1;
 
3420
  if (PRIVATE(this)->superimpositions == NULL) goto error;
 
3421
  idx = PRIVATE(this)->superimpositions->find(scene);
 
3422
  if (idx == -1) goto error;
 
3423
  PRIVATE(this)->superimpositionsenabled[idx] = enable;
 
3424
  return;
 
3425
 
 
3426
 error:
 
3427
  if (SO@GUI@_DEBUG) {
 
3428
    SoDebugError::post("So@Gui@Viewer::setSuperimpositionEnabled",
 
3429
                       "no such superimposition");
 
3430
  }
 
3431
  return;
 
3432
}
 
3433
 
 
3434
// *************************************************************************
 
3435
 
 
3436
/*!
 
3437
  This method returns whether the superimposed scene is rendered or not.
 
3438
 
 
3439
  This method is not part of the original InventorXt API.
 
3440
*/
 
3441
 
 
3442
SbBool
 
3443
So@Gui@Viewer::getSuperimpositionEnabled(SoNode * scene) const
 
3444
{
 
3445
  int idx = -1;
 
3446
  if (PRIVATE(this)->superimpositions == NULL) goto error;
 
3447
  idx = PRIVATE(this)->superimpositions->find(scene);
 
3448
  if (idx == -1) goto error;
 
3449
  return PRIVATE(this)->superimpositionsenabled[idx];
 
3450
 
 
3451
 error:
 
3452
  if (SO@GUI@_DEBUG) {
 
3453
    SoDebugError::post("So@Gui@Viewer::getSuperimpositionEnabled",
 
3454
                       "no such superimposition");
 
3455
  }
 
3456
  return FALSE;
 
3457
}
 
3458
 
 
3459
// *************************************************************************