1
/**************************************************************************\
3
* This file is part of the Coin 3D visualization library.
4
* Copyright (C) 1998-2003 by Systems in Motion. All rights reserved.
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.
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.
17
* See <URL:http://www.coin3d.org> for more information.
19
* Systems in Motion, Teknobyen, Abels Gate 5, 7030 Trondheim, NORWAY.
20
* <URL:http://www.sim.no>.
22
\**************************************************************************/
26
// NOTE: The So@Gui@Viewer.cpp sourcecode file is completely
27
// autogenerated from "templatized" source code.
29
// *************************************************************************
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
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.
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:
48
<li>camera type: toggle between using an orthographic camera and a
49
perspective camera with So@Gui@Viewer::toggleCameraType()</li>
51
<li>zoom out to exactly encompass all scene geometry within the view
52
by using So@Gui@Viewer::viewAll()</li>
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>
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>
63
<li>control stereo viewing parameters</li>
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()).
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):
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>
86
<li>"Home": hit this key to move camera back to last saved "home
89
<li>arrow keys: moves camera slightly left, right, up or down</li>
91
<li>"q": exit application</li>
96
// *************************************************************************
100
\enum So@Gui@Viewer::AutoClippingStrategy
101
Enum for auto clipping strategy.
103
\sa setAutoClippingStrategy()
107
\var So@Gui@Viewer::AutoClippingStrategy So@Gui@Viewer::CONSTANT_NEAR_PLANE
109
Constant near plane auto clipping strategy. Explained in detail in
110
the documentation for the So@Gui@Viewer::setAutoClippingStrategy()
115
\var So@Gui@Viewer::AutoClippingStrategy So@Gui@Viewer::VARIABLE_NEAR_PLANE
117
Variable near plane auto clipping strategy. Explained in detail in
118
the documentation for the So@Gui@Viewer::setAutoClippingStrategy()
124
#endif // HAVE_CONFIG_H
130
#include <Inventor/@Gui@/common/gl.h>
131
#include <Inventor/@Gui@/SoAny.h>
132
#include <Inventor/@Gui@/common/SbGuiList.h>
134
#include <Inventor/SoDB.h>
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>
149
#include <Inventor/actions/SoGetBoundingBoxAction.h>
150
#include <Inventor/actions/SoGetMatrixAction.h>
151
#include <Inventor/actions/SoSearchAction.h>
152
#include <Inventor/actions/SoRayPickAction.h>
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>
164
#include <so@gui@defs.h>
165
#include <Inventor/@Gui@/So@Gui@.h>
166
#include <Inventor/@Gui@/viewers/So@Gui@Viewer.h>
168
#include <float.h> // FLT_MAX
170
#if HAVE_SOPOLYGONOFFSET
171
#include <Inventor/nodes/SoPolygonOffset.h>
172
#endif // HAVE_SOPOLYGONOFFSET
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
179
// environment variables
180
static int COIN_SHOW_FPS_COUNTER = UNINITIALIZED_ENVVAR;
182
// *************************************************************************
184
#ifndef DOXYGEN_SKIP_THIS
186
// The private data for the So@Gui@Viewer.
187
class So@Gui@ViewerP {
189
So@Gui@ViewerP(So@Gui@Viewer * publ);
190
~So@Gui@ViewerP(void);
192
SoSeparator * createSuperScene(void);
193
SoSeparator * createFPSSuperimposition(void);
195
static void convertOrtho2Perspective(const SoOrthographicCamera * in,
196
SoPerspectiveCamera * out);
197
static void convertPerspective2Ortho(const SoPerspectiveCamera * in,
198
SoOrthographicCamera * out);
201
So@Gui@Viewer::Type type;
203
SoGetBoundingBoxAction * autoclipbboxaction;
204
SoSeparator * sceneroot;
208
// Seek functionality
209
SoTimerSensor * seeksensor;
213
SbVec3f camerastartposition, cameraendposition;
214
SbRotation camerastartorient, cameraendorient;
216
SbBool seekdistanceabs;
221
// Home position storage.
222
SoOrthographicCamera * storedortho;
223
SoPerspectiveCamera * storedperspective;
225
SoDirectionalLight * headlight;
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;
243
SoSeparator * fpsRoot;
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;
255
SbBool cursoron, localsetbuffertype;
256
SoCallbackList * interactionstartCallbacks, * interactionendCallbacks;
257
int interactionnesting;
258
So@Gui@Viewer::BufferType buffertype;
259
SbBool stereoviewing;
261
SbColor wireframeoverlaycolor;
263
void reallyRedraw(const SbBool clearcol, const SbBool clearz = TRUE);
265
// Seek functionality
266
static void seeksensorCB(void * data, SoSensor *);
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;
275
// Automatic setting of clipping planes
276
void setClippingPlanes(void);
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);
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);
290
SoSearchAction * searchaction;
291
SoGetMatrixAction * matrixaction;
292
SbPList * superimpositions;
293
SbGuiList<SbBool> superimpositionsenabled;
296
// auto clipping parameters
297
So@Gui@Viewer::AutoClippingStrategy autoclipstrategy;
299
So@Gui@AutoClippingCB * autoclipcb;
300
void * autoclipuserdata;
303
#define PRIVATE(ptr) (ptr->pimpl)
304
#define PUBLIC(ptr) (ptr->pub)
307
So@Gui@ViewerP::So@Gui@ViewerP(So@Gui@Viewer * publ)
310
this->searchaction = new SoSearchAction;
311
this->matrixaction = new SoGetMatrixAction(SbViewportRegion(100,100));
312
this->superimpositions = NULL;
314
this->storedortho = new SoOrthographicCamera;
315
this->storedortho->ref();
316
this->storedperspective = new SoPerspectiveCamera;
317
this->storedperspective->ref();
319
// initialize auto clipping parameters
320
this->autoclipstrategy = So@Gui@Viewer::VARIABLE_NEAR_PLANE;
321
this->autoclipvalue = 0.6f;
322
this->autoclipcb = NULL;
325
So@Gui@ViewerP::~So@Gui@ViewerP()
327
if (this->superimpositions) {
328
delete this->superimpositions;
329
this->superimpositions = NULL;
331
delete this->searchaction;
332
delete this->matrixaction;
334
this->storedortho->unref();
335
this->storedperspective->unref();
339
So@Gui@ViewerP::createSuperScene(void)
341
static const char * superSceneGraph[] =
343
"#Inventor V2.1 ascii",
346
" renderCaching OFF",
347
" renderCulling OFF",
349
" boundingBoxCaching OFF",
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",
357
" DEF so@gui@->drawstyleroot Switch {",
359
" DEF so@gui@->lightmodel LightModel {",
362
" DEF so@gui@->drawstyle DrawStyle {",
367
" DEF so@gui@->complexity Complexity {",
368
" textureQuality 0.0",
372
" DEF so@gui@->hiddenlineroot Switch {",
374
" DEF so@gui@->basecolor BaseColor { }",
375
" DEF so@gui@->materialbinding MaterialBinding {",
378
" DEF so@gui@->polygonoffsetparent Switch {",
380
#if HAVE_SOPOLYGONOFFSET
381
" DEF so@gui@->polygonoffset PolygonOffset { }",
382
#endif // HAVE_SOPOLYGONOFFSET
385
" DEF so@gui@->userscenegraphroot Separator {",
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]);
401
SoInput * input = new SoInput;
402
input->setBuffer(buf, bufsize);
403
SoNode * root = NULL;
404
SbBool ok = SoDB::read(input, root);
408
SoDebugError::post("So@Gui@Viewer::So@Gui@Viewer",
409
"couldn't create viewer superscene");
412
assert(root->isOfType(SoSeparator::getClassTypeId()));
415
this->searchaction->reset();
416
this->searchaction->setSearchingAll(TRUE);
417
this->searchaction->setInterest(SoSearchAction::FIRST);
419
#define LOCATE_NODE(member, type, name) \
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; \
430
SoDebugError::post("So@Gui@ViewerP::createSuperScene", \
431
"didn't locate node \"%s\"", name); \
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");
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
458
this->searchaction->reset();
460
root->unrefNoDelete();
461
return (SoSeparator *) root;
464
// FIXME: this method is unnecessary robust, and generally
465
// weird. 20020522 mortene.
467
So@Gui@ViewerP::createFPSSuperimposition(void)
469
static const char * fpsSceneGraph[] =
471
"#Inventor V2.1 ascii",
474
" renderCaching OFF",
475
" renderCulling OFF",
477
" boundingBoxCaching OFF",
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]);
492
SoInput * input = new SoInput;
493
input->setBuffer(buf, bufsize);
494
SoNode * root = NULL;
495
SbBool ok = SoDB::read(input, root);
499
SoDebugError::post("So@Gui@Viewer::So@Gui@Viewer",
500
"couldn't create viewer superscene");
503
assert(root->isOfType(SoSeparator::getClassTypeId()));
506
root->unrefNoDelete();
508
return (SoSeparator *) root;
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.
518
So@Gui@ViewerP::getCameraCoordinateSystem(SoCamera * camera,
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);
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();
535
this->searchaction->reset();
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
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
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
555
So@Gui@ViewerP::convertOrtho2Perspective(const SoOrthographicCamera * in,
556
SoPerspectiveCamera * out)
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());
564
float focaldist = in->focalDistance.getValue();
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);
571
// 45ļæ½ is the default value of this field in SoPerspectiveCamera.
572
out->heightAngle = (float)(M_PI / 4.0);
577
So@Gui@ViewerP::convertPerspective2Ortho(const SoPerspectiveCamera * in,
578
SoOrthographicCamera * out)
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());
586
float focaldist = in->focalDistance.getValue();
588
out->height = 2 * focaldist * tan(in->heightAngle.getValue() / 2.0f);
592
So@Gui@ViewerP::reallyRedraw(const SbBool clearcol, const SbBool clearz)
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(); }
599
if (this->drawAsHiddenLine()) {
601
// First pass: render as filled, but with the background color.
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
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;
614
PUBLIC(this)->getSceneManager()->render(clearcol, clearz);
616
// Second pass, render wireframe on top.
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;
624
PUBLIC(this)->getSceneManager()->render(FALSE, FALSE);
628
if (this->drawAsWireframeOverlay()) {
629
// First pass: render as-is, with polygon offset
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);
639
this->somaterialbinding->value.setIgnored(TRUE); // override with OVERALL
640
this->polygonoffsetparent->whichChild = SO_SWITCH_ALL;
642
PUBLIC(this)->getSceneManager()->render(clearcol, clearz);
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
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);
655
PUBLIC(this)->getSceneManager()->render(FALSE, FALSE);
657
// disable override nodes
658
(void) this->sobasecolor->rgb.enableNotify(FALSE);
659
this->sobasecolor->rgb.setIgnored(TRUE);
660
(void) this->sobasecolor->rgb.enableNotify(TRUE);
662
(void) this->somaterialbinding->value.enableNotify(FALSE);
663
this->somaterialbinding->value.setIgnored(TRUE);
664
(void) this->somaterialbinding->value.enableNotify(TRUE);
666
(void) this->solightmodel->model.enableNotify(FALSE);
667
this->solightmodel->model.setIgnored(TRUE);
668
(void) this->solightmodel->model.enableNotify(TRUE);
670
(void) this->socomplexity->textureQuality.enableNotify(FALSE);
671
this->socomplexity->textureQuality.setIgnored(TRUE);
672
(void) this->socomplexity->textureQuality.enableNotify(TRUE);
674
(void) this->sodrawstyle->style.enableNotify(FALSE);
675
this->sodrawstyle->style.setIgnored(TRUE);
676
(void) this->sodrawstyle->style.enableNotify(TRUE);
680
SbBool clearzbuffer = TRUE;
681
So@Gui@Viewer::DrawStyle style = this->currentDrawStyle();
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;
688
break; // Include "default:" case to avoid compiler warning.
691
PUBLIC(this)->getSceneManager()->render(clearcol, clearzbuffer && clearz);
695
// *************************************************************************
697
// Returns a boolean to indicate if the dynamic drawstyle equals
698
// the static drawstyle.
701
So@Gui@ViewerP::drawInteractiveAsStill(void) const
703
SbBool moveasstill = this->drawstyles[So@Gui@Viewer::INTERACTIVE] == So@Gui@Viewer::VIEW_SAME_AS_STILL;
705
moveasstill = this->drawstyles[So@Gui@Viewer::INTERACTIVE] == this->drawstyles[So@Gui@Viewer::STILL];
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;
713
// Returns the current drawing style.
714
So@Gui@Viewer::DrawStyle
715
So@Gui@ViewerP::currentDrawStyle(void) const
717
SbBool interactivemode = PUBLIC(this)->getInteractiveCount() > 0 ? TRUE : FALSE;
719
if (!interactivemode || this->drawInteractiveAsStill())
720
return this->drawstyles[So@Gui@Viewer::STILL];
722
return this->drawstyles[So@Gui@Viewer::INTERACTIVE];
725
// Returns a boolean to indicate if the current drawstyle settings implies
726
// hidden line rendering.
728
So@Gui@ViewerP::drawAsHiddenLine(void) const
730
return ((this->currentDrawStyle() == So@Gui@Viewer::VIEW_HIDDEN_LINE) ? TRUE : FALSE);
733
// Returns a boolean to indicate if the current drawstyle settings
734
// implies wirefram overlay rendering.
736
So@Gui@ViewerP::drawAsWireframeOverlay(void) const
738
return ((this->currentDrawStyle() == So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY) ? TRUE : FALSE);
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.
745
So@Gui@ViewerP::changeDrawStyle(So@Gui@Viewer::DrawStyle style)
747
// Turn on/off Z-buffering based on the style setting.
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();
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();
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;
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;
781
this->hiddenlineroot->whichChild = SO_SWITCH_NONE;
784
// Set or unset lightmodel override.
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
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
800
assert(FALSE); break;
804
// Set or unset drawstyle override.
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
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
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
825
assert(FALSE); break;
828
// Set or unset complexity value override.
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
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
844
assert(FALSE); break;
847
// Set or unset complexity textureQuality override (the value of the
848
// override-field is always 0.0, ie signalling "textures off").
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
861
this->socomplexity->textureQuality.setIgnored(TRUE); // don't override
865
// Set or unset complexity type override.
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
876
case So@Gui@Viewer::VIEW_BBOX:
877
this->socomplexity->type = SoComplexity::BOUNDING_BOX;
878
this->socomplexity->type.setIgnored(FALSE); // force bounding box rendering
882
assert(FALSE); break;
886
SoDebugError::postInfo("So@Gui@Viewer::changeDrawStyle",
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"
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");
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
909
// Near and far clipping planes are specified in the camera fields
910
// nearDistance and farDistance.
912
So@Gui@ViewerP::setClippingPlanes(void)
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;
918
if (this->autoclipbboxaction == NULL)
919
this->autoclipbboxaction =
920
new SoGetBoundingBoxAction(PUBLIC(this)->getViewportRegion());
922
this->autoclipbboxaction->setViewportRegion(PUBLIC(this)->getViewportRegion());
924
this->autoclipbboxaction->apply(this->sceneroot);
926
SbXfBox3f xbox = this->autoclipbboxaction->getXfBoundingBox();
930
this->getCameraCoordinateSystem(this->camera, this->sceneroot, cammat, inverse);
931
xbox.transform(inverse);
934
mat.setTranslate(- this->camera->position.getValue());
936
mat = this->camera->orientation.getValue().inverse();
938
SbBox3f box = xbox.project();
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
943
float nearval = -box.getMax()[2];
944
float farval = -box.getMin()[2];
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.
951
// Check if scene is completely behind us.
952
if (farval <= 0.0f) { return; }
954
if (this->camera->isOfType(SoPerspectiveCamera::getClassTypeId())) {
955
// Disallow negative and small near clipping plane distance.
957
float nearlimit; // the smallest value allowed for nearval
958
if (this->autoclipstrategy == So@Gui@Viewer::CONSTANT_NEAR_PLANE) {
959
nearlimit = this->autoclipvalue;
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
973
glGetIntegerv(GL_DEPTH_BITS, depthbits);
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;
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;
989
// adjust the near plane if the the value is too small.
990
if (nearval < nearlimit) {
994
if (this->autoclipcb) {
996
nearfar[0] = nearval;
999
nearfar = this->autoclipcb(this->autoclipuserdata, nearfar);
1001
nearval = nearfar[0];
1002
farval = nearfar[1];
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
1012
const float SLACK = 0.001f;
1014
this->camera->nearDistance = nearval * (1.0f - SLACK);
1015
this->camera->farDistance = farval * (1.0f + SLACK);
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":
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?
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.
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."
1040
// 20020117 mortene.
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());
1051
// Translate camera a distance equal to the difference in projected,
1052
// normalized screen coordinates given by the argument.
1054
So@Gui@ViewerP::moveCameraScreen(const SbVec2f & screenpos)
1056
SoCamera * cam = PUBLIC(this)->getCamera();
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]);
1068
SbViewVolume vv = cam->getViewVolume(PUBLIC(this)->getGLAspectRatio());
1069
SbPlane panplane = vv.getPlane(cam->focalDistance.getValue());
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);
1079
// Reposition camera according to the vector difference between the
1080
// projected points.
1081
cam->position = cam->position.getValue() - (current_planept - old_planept);
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]);
1092
// Called when viewer enters interactive mode (animation, drag, ...).
1094
So@Gui@ViewerP::interactivestartCB(void *, So@Gui@Viewer * thisp)
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;
1103
// Use the dynamic drawstyle.
1104
if (!PRIVATE(thisp)->drawInteractiveAsStill())
1105
PRIVATE(thisp)->changeDrawStyle(PRIVATE(thisp)->drawstyles[So@Gui@Viewer::INTERACTIVE]);
1108
// Called when viewer goes out of interactive mode and into "frozen"
1111
So@Gui@ViewerP::interactiveendCB(void *, So@Gui@Viewer * thisp)
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;
1121
// Back to static drawstyle.
1122
if (!PRIVATE(thisp)->drawInteractiveAsStill())
1123
PRIVATE(thisp)->changeDrawStyle(PRIVATE(thisp)->drawstyles[So@Gui@Viewer::STILL]);
1126
// Called repeatedly during the seek animation.
1128
So@Gui@ViewerP::seeksensorCB(void * data, SoSensor * s)
1130
SbTime currenttime = SbTime::getTimeOfDay();
1132
So@Gui@Viewer * thisp = (So@Gui@Viewer *)data;
1133
SoTimerSensor * sensor = (SoTimerSensor *)s;
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);
1140
t = (float) ((1.0 - cos(M_PI*t)) * 0.5);
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,
1149
if (end) thisp->setSeekMode(FALSE);
1152
// Reset the frames-per-second counter upon window resize events,
1153
// abnormal delays, etc.
1155
// The methods for recording FPS values are Coin extensions, not
1156
// available in the original Open Inventor API.
1158
// \sa addFrametime(), recordFPS()
1160
So@Gui@ViewerP::resetFrameCounter(void)
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();
1170
// Adds the time spent drawing the last frame to the array of past
1171
// frame times. Returns the current averaged fps-value.
1173
// The methods for recording FPS values are Coin extensions, not
1174
// available in the original Open Inventor API.
1176
// \sa resetFrameCounter(), recordFPS()
1178
So@Gui@ViewerP::addFrametime(const double ft)
1182
int arrayptr = (this->framecount - 1) % FRAMESARRAY_SIZE;
1184
this->totalcoin += (ft - this->frames[arrayptr][0]);
1186
this->totalcoin / So@Gui@Min(this->framecount, (int) FRAMESARRAY_SIZE);
1188
double timeofday = SbTime::getTimeOfDay().getValue();
1189
double ct = timeofday - this->lastgettimeofday;
1190
this->totaldraw += (ct - this->frames[arrayptr][1]);
1192
this->totaldraw / So@Gui@Min(this->framecount, (int) FRAMESARRAY_SIZE);
1194
this->frames[arrayptr] = SbVec2f(ft, ct);
1195
this->lastgettimeofday = timeofday;
1197
return SbVec2f(1.0f / coinfps, 1.0f / drawfps);
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 } // ~
1299
printString(const char * s)
1303
for (i = 0; i < n; i++)
1304
glBitmap(8, 12, 0.0, 2.0, 10.0, 0.0, fps2dfont[s[i] - 32]);
1308
Draw2DString(const char * str, SbVec2s glsize, SbVec2f position)
1311
glPushAttrib(GL_ALL_ATTRIB_BITS);
1313
glDisable(GL_LIGHTING);
1314
glDisable(GL_DEPTH_TEST);
1315
glDisable(GL_TEXTURE_2D);
1316
glMatrixMode(GL_MODELVIEW);
1320
glMatrixMode(GL_PROJECTION);
1323
glOrtho(0.0, glsize[0], 0.0, glsize[1], -1, 1);
1325
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1327
glColor3f(0.0, 0.0, 0.0);
1328
glRasterPos2f(position[0] + 1, position[1]);
1330
glRasterPos2f(position[0] - 1, position[1]);
1332
glRasterPos2f(position[0], position[1] + 1);
1334
glRasterPos2f(position[0], position[1] - 1);
1337
glColor3f(1.0, 1.0, 0.0);
1338
glRasterPos2f(position[0], position[1]);
1341
glMatrixMode(GL_PROJECTION);
1343
glMatrixMode(GL_MODELVIEW);
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.
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.
1358
// 20001124 mortene.
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).
1364
// The methods for recording FPS values are Coin extensions, not
1365
// available in the original Open Inventor API.
1367
// \sa resetFrameCounter(), addFrametime()
1369
So@Gui@ViewerP::recordFPS(const double rendertime)
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;
1376
if (COIN_SHOW_FPS_COUNTER) {
1377
SbVec2f fps = this->addFrametime(rendertime);
1380
int nr = sprintf(buffer, "%.1f/%.1f fps", fps[0], fps[1]);
1382
Draw2DString(buffer, PUBLIC(this)->getGLSize(), SbVec2f(10, 10));
1386
#endif // DOXYGEN_SKIP_THIS
1388
// *************************************************************************
1390
SO@GUI@_OBJECT_ABSTRACT_SOURCE(So@Gui@Viewer);
1392
// *************************************************************************
1395
\enum So@Gui@Viewer::Type
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
1404
\var So@Gui@Viewer::Type So@Gui@Viewer::BROWSER
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
1413
\var So@Gui@Viewer::Type So@Gui@Viewer::EDITOR
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.
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.
1426
\enum So@Gui@Viewer::DrawType
1428
Contains valid values for the first argument to the
1429
So@Gui@Viewer::setDrawStyle() call. Decides the effect of the second
1432
\sa So@Gui@Viewer::setDrawStyle(), So@Gui@Viewer::DrawStyle
1435
\var So@Gui@Viewer::DrawType So@Gui@Viewer::STILL
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.
1444
\var So@Gui@Viewer::DrawType So@Gui@Viewer::INTERACTIVE
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.
1453
\enum So@Gui@Viewer::DrawStyle
1455
Decides drawstyle for a scene with either a still camera or an
1458
\sa So@Gui@Viewer::setDrawStyle(), So@Gui@Viewer::DrawType
1461
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_AS_IS
1463
Normal rendering, draws all scene geometry in it's original style.
1466
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_HIDDEN_LINE
1468
Draw scene in "hidden line" mode: that is, as wireframe with no
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.
1476
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY
1478
Render the scene as normal, but overlay a set of lines showing the
1479
contours of all polygons.
1482
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_NO_TEXTURE
1484
Render scene without textures.
1487
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_COMPLEXITY
1489
Render all "complex" shape types with low complexity to improve
1490
rendering performance.
1492
"Complex shapes" in this context includes spheres, cones, cylinder,
1493
NURBS surfaces, and others which are tesselated to polygons before
1497
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LINE
1499
View all polygon geometry in wireframe mode.
1502
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_POINT
1504
Render only the vertex positions of the geometry.
1507
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_BBOX
1509
View the scene's bounding boxes, instead of rendering the full
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.
1517
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_RES_LINE
1519
Render as wireframe and don't bother with getting them rendered
1523
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_LOW_RES_POINT
1525
Render as vertex points and don't bother with getting them rendered
1529
\var So@Gui@Viewer::DrawStyle So@Gui@Viewer::VIEW_SAME_AS_STILL
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.
1537
\enum So@Gui@Viewer::BufferType
1538
Set of valid values for So@Gui@Viewer::setBufferingType().
1541
\var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_SINGLE
1542
Change underlying OpenGL canvas to be single-buffered.
1545
\var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_DOUBLE
1546
Change underlying OpenGL canvas to be double-buffered.
1549
\var So@Gui@Viewer::BufferType So@Gui@Viewer::BUFFER_INTERACTIVE
1551
Set up so animation rendering is done in a double-buffered OpenGL
1552
canvas, but ordinary rendering happens directly in the front-buffer.
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.
1560
// *************************************************************************
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.
1571
get_parent_of_node(So@Gui@ViewerP * pimpl, SoNode * root, SoNode * node)
1573
SbBool oldsearch = SoBaseKit::isSearchingChildren();
1574
SoBaseKit::setSearchingChildren(TRUE);
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);
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;
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");
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;
1602
// *************************************************************************
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.
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.)
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
1620
So@Gui@Viewer::So@Gui@Viewer(@WIDGET@ parent,
1623
So@Gui@Viewer::Type t,
1625
: inherited(parent, name, embed, TRUE, TRUE, FALSE)
1627
PRIVATE(this) = new So@Gui@ViewerP(this);
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;
1635
// initialization of internal data
1636
PRIVATE(this)->cursoron = TRUE;
1637
PRIVATE(this)->localsetbuffertype = FALSE;
1639
PRIVATE(this)->cameratype = SoPerspectiveCamera::getClassTypeId();
1640
PRIVATE(this)->deletecamera = FALSE;
1641
PRIVATE(this)->buffertype = this->isDoubleBuffer() ? BUFFER_DOUBLE : BUFFER_SINGLE;
1643
PRIVATE(this)->interactionstartCallbacks = new SoCallbackList;
1644
PRIVATE(this)->interactionendCallbacks = new SoCallbackList;
1645
PRIVATE(this)->interactionnesting = 0;
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);
1654
PRIVATE(this)->sceneroot = PRIVATE(this)->createSuperScene();
1655
PRIVATE(this)->sceneroot->ref();
1657
PRIVATE(this)->drawstyles[STILL] = VIEW_AS_IS;
1658
PRIVATE(this)->drawstyles[INTERACTIVE] = VIEW_SAME_AS_STILL;
1660
this->addStartCallback(So@Gui@ViewerP::interactivestartCB);
1661
this->addFinishCallback(So@Gui@ViewerP::interactiveendCB);
1663
PRIVATE(this)->adjustclipplanes = TRUE;
1664
PRIVATE(this)->autoclipbboxaction = NULL;
1666
PRIVATE(this)->stereoviewing = FALSE;
1667
PRIVATE(this)->stereooffset = 0.1f;
1669
PRIVATE(this)->wireframeoverlaycolor = SbColor(1.0f, 0.0f, 0.0f);
1672
this->setClassName("So@Gui@Viewer");
1673
@WIDGET@ widget = this->buildWidget(this->getParentWidget());
1674
this->setBaseWidget(widget);
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);
1682
PRIVATE(this)->resetFrameCounter();
1685
// *************************************************************************
1691
So@Gui@Viewer::~So@Gui@Viewer()
1693
delete PRIVATE(this)->autoclipbboxaction;
1695
delete PRIVATE(this)->interactionstartCallbacks;
1696
delete PRIVATE(this)->interactionendCallbacks;
1698
delete PRIVATE(this)->seeksensor;
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];
1708
delete PRIVATE(this);
1711
// *************************************************************************
1713
// Note: the following function documentation block will also be used
1714
// for all the miscellaneous viewer subclasses, so keep it general.
1716
Set the camera we want to view the scene with.
1718
The camera passed in as an argument to this method \e must be part
1719
of the viewer's scenegraph.
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.
1729
So@Gui@Viewer::setCamera(SoCamera * cam)
1731
if (PRIVATE(this)->camera) {
1732
// If we made the camera, detach it. Otherwise just leave it in
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;
1741
PRIVATE(this)->camera->unref();
1744
PRIVATE(this)->camera = cam;
1746
if (PRIVATE(this)->camera) {
1747
PRIVATE(this)->camera->ref();
1748
this->saveHomePosition();
1749
PRIVATE(this)->cameratype = PRIVATE(this)->camera->getTypeId();
1753
// *************************************************************************
1756
Returns the camera currently used by the viewer for the user's main
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.)
1770
So@Gui@Viewer::getCamera(void) const
1772
return PRIVATE(this)->camera;
1775
// *************************************************************************
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.
1782
Default is to use an SoPerspectiveCamera.
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
1793
So@Gui@Viewer::setCameraType(SoType t)
1795
SoType perspectivetype = SoPerspectiveCamera::getClassTypeId();
1796
SoType orthotype = SoOrthographicCamera::getClassTypeId();
1797
SbBool oldisperspective = PRIVATE(this)->cameratype.isDerivedFrom(perspectivetype);
1798
SbBool newisperspective = t.isDerivedFrom(perspectivetype);
1800
if ((oldisperspective && newisperspective) ||
1801
(!oldisperspective && !newisperspective)) // Same old, same old..
1804
if (SO@GUI@_DEBUG) {
1805
SbBool valid = TRUE;
1806
if (t == SoType::badType()) valid = FALSE;
1809
if (newisperspective) valid = TRUE;
1810
if (t.isDerivedFrom(orthotype)) valid = TRUE;
1814
SoDebugError::post("So@Gui@Viewer::setCameraType",
1815
"not a valid camera type: '%s'",
1816
t == SoType::badType() ?
1817
"badType" : t.getName().getString());
1822
if (PRIVATE(this)->camera != NULL) {
1823
SoCamera * newcamera = (SoCamera *)t.createInstance();
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);
1831
So@Gui@ViewerP::convertPerspective2Ortho((SoPerspectiveCamera *)PRIVATE(this)->camera,
1832
(SoOrthographicCamera *)newcamera);
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;
1841
// Store the current home position, as it will be implicitly reset
1843
SoOrthographicCamera * homeo = new SoOrthographicCamera;
1844
SoPerspectiveCamera * homep = new SoPerspectiveCamera;
1847
homeo->copyContents(PRIVATE(this)->storedortho, FALSE);
1848
homep->copyContents(PRIVATE(this)->storedperspective, FALSE);
1850
this->setCamera(newcamera); // This will set PRIVATE(this)->cameratype.
1852
// Restore home position.
1853
PRIVATE(this)->storedortho->copyContents(homeo, FALSE);
1854
PRIVATE(this)->storedperspective->copyContents(homep, FALSE);
1858
PRIVATE(this)->deletecamera = TRUE;
1859
if (oldcamera) { cameraparent->removeChild(oldcamera); }
1861
else { // A camera has not been instantiated for the scene.
1862
PRIVATE(this)->cameratype = t; // No call to setCamera(), so set type explicitly.
1866
// *************************************************************************
1869
Returns camera type which will be used when the viewer has to make its
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.
1879
So@Gui@Viewer::getCameraType(void) const
1881
return PRIVATE(this)->cameratype;
1884
// *************************************************************************
1887
Reposition the current camera so we can see the complete scene.
1891
So@Gui@Viewer::viewAll(void)
1893
if (PRIVATE(this)->camera && PRIVATE(this)->scenegraph)
1894
PRIVATE(this)->camera->viewAll(PRIVATE(this)->scenegraph, this->getViewportRegion());
1897
// *************************************************************************
1900
Store the current camera settings for later retrieval with
1901
resetToHomePosition().
1903
\sa resetToHomePosition()
1907
So@Gui@Viewer::saveHomePosition(void)
1909
if (! PRIVATE(this)->camera) return; // probably a scene-less viewer
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).
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
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);
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);
1936
SoDebugError::postWarning("So@Gui@Viewer::saveHomePosition",
1937
"Only SoPerspectiveCamera and SoOrthographicCamera is supported.");
1938
#endif // SO@GUI_DEBUG
1942
// *************************************************************************
1945
Restore the saved camera settings.
1947
\sa saveHomePosition()
1951
So@Gui@Viewer::resetToHomePosition(void)
1953
if (! PRIVATE(this)->camera) return; // probably a scene-less viewer
1955
SoType t = PRIVATE(this)->camera->getTypeId();
1956
if (t == SoOrthographicCamera::getClassTypeId()) {
1957
PRIVATE(this)->camera->copyContents(PRIVATE(this)->storedortho, FALSE);
1959
else if (t == SoPerspectiveCamera::getClassTypeId()) {
1960
PRIVATE(this)->camera->copyContents(PRIVATE(this)->storedperspective, FALSE);
1964
// *************************************************************************
1967
Turn the camera headlight on or off.
1969
Default is to have a headlight turned on.
1971
\sa isHeadlight(), getHeadlight()
1975
So@Gui@Viewer::setHeadlight(SbBool on)
1977
PRIVATE(this)->headlight->on = on;
1980
// *************************************************************************
1983
Returns status of the viewer headlight, whether it is on or off.
1985
\sa setHeadlight(), getHeadlight()
1989
So@Gui@Viewer::isHeadlight(void) const
1991
return PRIVATE(this)->headlight->on.getValue();
1994
// *************************************************************************
1997
Returns the a pointer to the directional light node which is the
2000
The fields of the node is available for user editing.
2002
\sa isHeadlight(), setHeadlight()
2005
SoDirectionalLight *
2006
So@Gui@Viewer::getHeadlight(void) const
2008
return PRIVATE(this)->headlight;
2011
// *************************************************************************
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.
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.
2021
See the documentation for the \a DrawType and \a DrawStyle for more
2027
So@Gui@Viewer::setDrawStyle(So@Gui@Viewer::DrawType type,
2028
So@Gui@Viewer::DrawStyle style)
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);
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",
2047
PRIVATE(this)->drawstyles[type] = style;
2048
PRIVATE(this)->changeDrawStyle(PRIVATE(this)->currentDrawStyle());
2051
// *************************************************************************
2054
Return current drawstyles for the given type (\a STILL or
2060
So@Gui@Viewer::DrawStyle
2061
So@Gui@Viewer::getDrawStyle(const So@Gui@Viewer::DrawType type) const
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];
2071
return PRIVATE(this)->drawstyles[type];
2074
// *************************************************************************
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.
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.)
2085
Default is \c So@Gui@Viewer::BUFFER_DOUBLE.
2087
\sa getBufferingType()
2091
So@Gui@Viewer::setBufferingType(So@Gui@Viewer::BufferType type)
2093
if (type == PRIVATE(this)->buffertype) return;
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);
2105
PRIVATE(this)->buffertype = type;
2107
PRIVATE(this)->localsetbuffertype = TRUE;
2108
inherited::setDoubleBuffer(type == BUFFER_DOUBLE);
2109
PRIVATE(this)->localsetbuffertype = FALSE;
2112
// *************************************************************************
2115
Return the viewer's buffer type.
2117
\sa setBufferingType()
2120
So@Gui@Viewer::BufferType
2121
So@Gui@Viewer::getBufferingType(void) const
2123
return PRIVATE(this)->buffertype;
2126
// *************************************************************************
2128
// Note: this documentation for setViewing() will also be used for all
2129
// the miscellaneous viewer subclasses, so keep it general.
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.
2138
Default is to have the view mode active.
2143
So@Gui@Viewer::setViewing(SbBool enable)
2145
if (PRIVATE(this)->viewingflag == enable) {
2146
if (SO@GUI@_DEBUG) {
2147
SoDebugError::postWarning("So@Gui@Viewer::setViewing",
2148
"unnecessary called");
2153
PRIVATE(this)->viewingflag = enable;
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();
2160
SoLocateHighlight::turnOffCurrentHighlight(action);
2164
// *************************************************************************
2167
Return state of view mode.
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.
2176
So@Gui@Viewer::isViewing(void) const
2178
return PRIVATE(this)->viewingflag;
2181
// *************************************************************************
2184
Set whether or not the mouse cursor representation should be visible
2185
in the viewer canvas.
2187
Default value is on.
2189
\sa isCursorEnabled()
2193
So@Gui@Viewer::setCursorEnabled(SbBool on)
2195
PRIVATE(this)->cursoron = on;
2198
// *************************************************************************
2201
Returns visibility status of mouse cursor.
2203
\sa setCursorEnabled()
2207
So@Gui@Viewer::isCursorEnabled(void) const
2209
return PRIVATE(this)->cursoron;
2212
// *************************************************************************
2215
Turn on or off continuous automatic adjustments of the near and far
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
2226
Automatic clipping is on as default.
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.
2234
\sa getAutoClipping()
2238
So@Gui@Viewer::setAutoClipping(SbBool enable)
2240
if (SO@GUI@_DEBUG) {
2241
if (PRIVATE(this)->adjustclipplanes == enable) {
2242
SoDebugError::postWarning("So@Gui@Viewer::setAutoClipping",
2243
"unnecessary called");
2248
PRIVATE(this)->adjustclipplanes = enable;
2249
if (enable) { this->scheduleRedraw(); }
2253
Set the auto clipping strategy used for auto clipping.
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
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.
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.
2273
The default strategy is VARIABLE_NEAR_PLANE.
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.
2284
The signature of the So@Gui@AutoClippingCB callback must match:
2286
SbVec2f myfunc(void * data, const SbVec2f & nearfar);
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
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.
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).
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
2312
\sa setAutoClipping()
2315
So@Gui@Viewer::setAutoClippingStrategy(const AutoClippingStrategy strategy,
2317
So@Gui@AutoClippingCB * cb,
2320
PRIVATE(this)->autoclipstrategy = strategy;
2321
PRIVATE(this)->autoclipvalue = value;
2322
PRIVATE(this)->autoclipcb = cb;
2323
PRIVATE(this)->autoclipuserdata = cbuserdata;
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
2331
v += 0.1f; // v will be in range [0.1, 0.9]
2333
PRIVATE(this)->autoclipvalue = v;
2335
if (PRIVATE(this)->adjustclipplanes) {
2336
this->scheduleRedraw();
2340
// *************************************************************************
2343
Return value of the automatic near/far clipplane adjustment indicator.
2345
\sa setAutoClipping()
2349
So@Gui@Viewer::isAutoClipping(
2352
return PRIVATE(this)->adjustclipplanes;
2355
// *************************************************************************
2358
Turn stereo viewing on or off.
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:
2364
http://astronomy.swin.edu.au/~pbourke/opengl/stereogl/
2366
\sa isStereoViewing()
2370
So@Gui@Viewer::setStereoViewing(// virtual
2373
PRIVATE(this)->stereoviewing = enable;
2374
this->scheduleRedraw();
2377
// *************************************************************************
2380
Returns a boolean indicating whether or not we're in stereo viewing
2383
NOTE: in the original InventorXt API, this method was virtual. It is not
2386
\sa setStereoViewing()
2390
So@Gui@Viewer::isStereoViewing(
2393
return PRIVATE(this)->stereoviewing;
2396
// *************************************************************************
2399
Set the offset between the two viewpoints when in stereo mode.
2401
NOTE: In the original InventorXt API, this method was not virtual.
2403
\sa getStereoOffset()
2407
So@Gui@Viewer::setStereoOffset(// virtual
2410
PRIVATE(this)->stereooffset = dist;
2411
this->scheduleRedraw();
2415
Return the offset distance between the two viewpoints when in stereo mode.
2417
\sa setStereoOffset()
2421
So@Gui@Viewer::getStereoOffset(
2424
return PRIVATE(this)->stereooffset;
2427
// *************************************************************************
2430
Toggle between seeking to a point or seeking to an object.
2432
Default is to seek to a point.
2438
So@Gui@Viewer::setDetailSeek(const SbBool on)
2440
if (SO@GUI@_DEBUG) {
2441
if (PRIVATE(this)->seektopoint == on) {
2442
SoDebugError::postWarning("So@Gui@Viewer::setDetailSeek",
2443
"unnecessary called");
2448
PRIVATE(this)->seektopoint = on;
2451
// *************************************************************************
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.
2461
So@Gui@Viewer::isDetailSeek(void) const
2463
return PRIVATE(this)->seektopoint;
2466
// *************************************************************************
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.
2473
Default value is 2 seconds.
2479
So@Gui@Viewer::setSeekTime(const float seconds)
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 "
2489
PRIVATE(this)->seekperiod = seconds;
2492
// *************************************************************************
2495
Returns the camera repositioning duration following a seek action.
2501
So@Gui@Viewer::getSeekTime(void) const
2503
return PRIVATE(this)->seekperiod;
2506
// *************************************************************************
2509
Add a function to call when user interaction with the scene starts.
2511
\sa removeStartCallback(), addFinishCallback()
2515
So@Gui@Viewer::addStartCallback(So@Gui@ViewerCB * func, void * data)
2517
PRIVATE(this)->interactionstartCallbacks->addCallback((SoCallbackListCB *)func, data);
2521
Remove one of the functions which has been set up to be called when user
2522
interaction with the scene starts.
2524
\sa addStartCallback(), removeFinishCallback()
2528
So@Gui@Viewer::removeStartCallback(So@Gui@ViewerCB * func, void * data)
2530
PRIVATE(this)->interactionstartCallbacks->removeCallback((SoCallbackListCB *)func,
2534
// *************************************************************************
2537
Add a function to call when user interaction with the scene ends.
2539
\sa removeFinishCallback(), addStartCallback()
2543
So@Gui@Viewer::addFinishCallback(So@Gui@ViewerCB * func, void * data)
2545
PRIVATE(this)->interactionendCallbacks->addCallback((SoCallbackListCB *)func, data);
2549
Remove one of the functions which has been set up to be called when user
2550
interaction with the scene ends.
2552
\sa addFinishCallback(), removeStartCallback()
2556
So@Gui@Viewer::removeFinishCallback(So@Gui@ViewerCB * func, void * data)
2558
PRIVATE(this)->interactionendCallbacks->removeCallback((SoCallbackListCB *)func,
2562
// *************************************************************************
2565
Set the color of the overlay wireframe to \a color.
2567
\sa getWireframeOverlayColor()
2569
void So@Gui@Viewer::setWireframeOverlayColor(const SbColor & color)
2571
PRIVATE(this)->wireframeoverlaycolor = color;
2572
this->scheduleRedraw();
2575
// *************************************************************************
2578
Returns the current color of the overlay wireframe. The default
2579
color is [1,0,0], ie pure red.
2581
\sa setWireframeOverlayColor()
2583
const SbColor &So@Gui@Viewer::getWireframeOverlayColor(void) const
2585
return PRIVATE(this)->wireframeoverlaycolor;
2588
// *************************************************************************
2591
Overloaded to update the local bufferingtype variable.
2593
\sa setBufferingType(), getBufferingType()
2597
So@Gui@Viewer::setDoubleBuffer(const SbBool on)
2599
if (!PRIVATE(this)->localsetbuffertype)
2600
PRIVATE(this)->buffertype = on ? BUFFER_DOUBLE : BUFFER_SINGLE;
2602
inherited::setDoubleBuffer(on);
2605
// *************************************************************************
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.
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.
2616
If no camera is part of the scene graph under \a root, one will be
2617
added automatically.
2619
\sa getSceneGraph(), setCameraType()
2623
So@Gui@Viewer::setSceneGraph(SoNode * root)
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");
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);
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);
2647
PRIVATE(this)->scenegraph = root;
2650
PRIVATE(this)->usersceneroot->addChild(PRIVATE(this)->scenegraph);
2651
// old: PRIVATE(this)->sceneroot->addChild(PRIVATE(this)->scenegraph);
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);
2660
PRIVATE(this)->searchaction->reset();
2661
PRIVATE(this)->searchaction->setType(SoCamera::getClassTypeId());
2662
PRIVATE(this)->searchaction->apply(PRIVATE(this)->scenegraph);
2664
SoBaseKit::setSearchingChildren(oldsearch);
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();
2674
SoDebugError::postInfo("So@Gui@Viewer::setSceneGraph",
2675
"camera %sfound in graph",
2676
scenecamera ? "" : "not ");
2679
// Make our own camera if none was available.
2681
scenecamera = (SoCamera *) PRIVATE(this)->cameratype.createInstance();
2682
PRIVATE(this)->deletecamera = TRUE;
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);
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
2694
((SoGroup *)PRIVATE(this)->scenegraph)->insertChild(scenecamera, 0);
2697
// Make an extra depth level to fit the camera node into the
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;
2708
scenecamera->viewAll(PRIVATE(this)->scenegraph, this->getViewportRegion());
2711
this->setCamera(scenecamera);
2714
// *************************************************************************
2718
So@Gui@Viewer::getSceneGraph(void)
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;
2726
// *************************************************************************
2728
// Note: the following function documentation block will also be used
2729
// for all the miscellaneous viewer subclasses, so keep it general.
2731
Put the viewer in or out of "waiting-to-seek" mode.
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
2738
\sa isSeekMode(), setDetailSeek()
2741
So@Gui@Viewer::setSeekMode(SbBool enable)
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()); }
2749
if (!enable && PRIVATE(this)->seeksensor->isScheduled()) {
2750
PRIVATE(this)->seeksensor->unschedule();
2751
this->interactiveCountDec();
2754
PRIVATE(this)->inseekmode = enable;
2757
// *************************************************************************
2760
Return a flag which indicates whether or not the viewer is in
2761
"waiting-to-seek" mode.
2763
(The actual animated translation will not occur until the end user
2764
really \e starts the seek operation, typically by clicking with the
2770
So@Gui@Viewer::isSeekMode(void) const
2772
return PRIVATE(this)->inseekmode;
2775
// *************************************************************************
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.
2782
Returns \c TRUE if the ray from the \a screenpos position intersect
2783
with any parts of the onscreen geometry, otherwise \c FALSE.
2786
So@Gui@Viewer::seekToPoint(const SbVec2s screenpos)
2788
if (! PRIVATE(this)->camera)
2791
SoRayPickAction rpaction(this->getViewportRegion());
2792
rpaction.setPoint(screenpos);
2793
rpaction.setRadius(2);
2794
rpaction.apply(PRIVATE(this)->sceneroot);
2796
SoPickedPoint * picked = rpaction.getPickedPoint();
2798
// FIXME: this inc seems bogus, but is needed now due to buggy
2799
// code in for instance the examinerviewer
2800
// processSoEvent(). 20020510 mortene.
2802
this->interactiveCountInc(); // decremented in setSeekMode(FALSE)
2804
this->setSeekMode(FALSE);
2809
if (PRIVATE(this)->seektopoint) {
2810
hitpoint = picked->getPoint();
2813
SoGetBoundingBoxAction bbaction(this->getViewportRegion());
2814
bbaction.apply(picked->getPath());
2815
SbBox3f bbox = bbaction.getBoundingBox();
2816
hitpoint = bbox.getCenter();
2819
PRIVATE(this)->camerastartposition = PRIVATE(this)->camera->position.getValue();
2820
PRIVATE(this)->camerastartorient = PRIVATE(this)->camera->orientation.getValue();
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,
2829
camerainverse.multVecMatrix(hitpoint, hitpoint);
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;
2836
SbVec3f dir = hitpoint - PRIVATE(this)->camerastartposition;
2839
// find a rotation that rotates current camera direction into new
2840
// camera direction.
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;
2847
if (PRIVATE(this)->seeksensor->isScheduled()) {
2848
PRIVATE(this)->seeksensor->unschedule();
2849
this->interactiveCountDec();
2852
PRIVATE(this)->seeksensor->setBaseTime(SbTime::getTimeOfDay());
2853
PRIVATE(this)->seeksensor->schedule();
2854
this->interactiveCountInc();
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
2863
So@Gui@Viewer::actualRedraw(void)
2865
SbTime redrawtime = SbTime::getTimeOfDay();
2867
SbBool clearcol = this->isClearBeforeRender();
2869
if (this->isStereoViewing()) {
2870
SbColor bgcol = this->getSceneManager()->getBackgroundColor();
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();
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);
2892
camera->position = camerapos - offsetvec * offset;
2893
SbVec3f dir = focalpoint - camera->position.getValue();
2894
SbRotation rot(cameradir, dir);
2895
camera->orientation = camerarot * rot;
2897
glDrawBuffer(this->isDoubleBuffer() ? GL_BACK_LEFT : GL_FRONT_LEFT);
2898
PRIVATE(this)->reallyRedraw(clearcol);
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);
2909
camera->position = camerapos + offsetvec * offset;
2910
dir = focalpoint - camera->position.getValue();
2911
rot.setValue(cameradir, dir);
2912
camera->orientation = camerarot * rot;
2915
glDrawBuffer(this->isDoubleBuffer() ? GL_BACK_RIGHT : GL_FRONT_RIGHT);
2916
PRIVATE(this)->reallyRedraw(clearcol);
2919
glColorMask(0,1,1,1); // draw green and blue
2920
PRIVATE(this)->reallyRedraw(FALSE, TRUE);
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);
2930
glDrawBuffer(this->isDoubleBuffer() ? GL_BACK : GL_FRONT);
2933
glColorMask(1,1,1,1); // restore GL color mask
2935
#else // HAVE_SOCAMERA_SETSTEREOMODE
2938
SoCamera * camera = this->getCamera();
2939
if (this->isDoubleBuffer())
2940
glDrawBuffer(GL_BACK);
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);
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
2957
SoCamera * camera = this->getCamera();
2958
if (this->isDoubleBuffer())
2959
glDrawBuffer(GL_BACK_LEFT);
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);
2969
glDrawBuffer(GL_FRONT_RIGHT);
2970
PRIVATE(this)->reallyRedraw(clearcol);
2971
camera->setStereoMode(SoCamera::MONOSCOPIC);
2972
if (this->isDoubleBuffer())
2973
glDrawBuffer(GL_BACK);
2975
glDrawBuffer(GL_FRONT);
2977
#endif // HAVE_SOCAMERA_SETSTEREOMODE
2979
else PRIVATE(this)->reallyRedraw(clearcol);
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) {
2988
// save Z buffer state and disable
2989
zWasEnabled = glIsEnabled(GL_DEPTH_TEST) ? TRUE : FALSE;
2990
glDisable(GL_DEPTH_TEST);
2993
SoNode * scene = (SoNode *) (*PRIVATE(this)->superimpositions)[i];
2994
raaction->apply(scene);
2997
if (!first && zWasEnabled) glEnable(GL_DEPTH_TEST);
3000
redrawtime = SbTime::getTimeOfDay() - redrawtime;
3001
PRIVATE(this)->recordFPS(redrawtime.getValue());
3004
// *************************************************************************
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?).
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.
3015
\sa interactiveCountDec(), getInteractiveCount()
3016
\sa addStartCallback(), addFinishCallback()
3017
\sa removeStartCallback(), removeFinishCallback()
3018
\sa setDrawStyle(), setBufferingType()
3022
So@Gui@Viewer::interactiveCountInc(void)
3024
// Catch problems with missing interactiveCountDec() calls.
3025
assert(PRIVATE(this)->interactionnesting < 100);
3027
if (++(PRIVATE(this)->interactionnesting) == 1) {
3028
PRIVATE(this)->interactionstartCallbacks->invokeCallbacks(this);
3029
PRIVATE(this)->resetFrameCounter();
3033
SoDebugError::postInfo("So@Gui@Viewer::interactiveCountInc", "%d -> %d",
3034
PRIVATE(this)->interactionnesting - 1,
3035
PRIVATE(this)->interactionnesting);
3039
// *************************************************************************
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?).
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.
3050
\sa interactiveCountInc(), getInteractiveCount()
3051
\sa addStartCallback(), addFinishCallback()
3052
\sa removeStartCallback(), removeFinishCallback()
3053
\sa setDrawStyle(), setBufferingType()
3057
So@Gui@Viewer::interactiveCountDec(void)
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.
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.
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.
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.
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.");
3088
if (--(PRIVATE(this)->interactionnesting) <= 0) {
3089
PRIVATE(this)->interactionendCallbacks->invokeCallbacks(this);
3090
PRIVATE(this)->interactionnesting = 0;
3094
// *************************************************************************
3097
Return current interaction count nesting. If equal to zero, the viewer
3098
is in animation mode, otherwise the camera is still.
3100
\sa interactiveCountInc(), interactiveCountDec()
3104
So@Gui@Viewer::getInteractiveCount(void) const
3106
return PRIVATE(this)->interactionnesting;
3109
// *************************************************************************
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.
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
3122
Default value is 50 (absolute distance or percent).
3124
\sa getSeekDistance(), setSeekValueAsPercentage(), setSeekTime()
3128
So@Gui@Viewer::setSeekDistance(const float distance)
3130
if (distance <= 0.0f) {
3131
if (SO@GUI@_DEBUG) {
3132
SoDebugError::postWarning("So@Gui@Viewer::setSeekDistance",
3133
"invalid seek distance value: %f",
3138
PRIVATE(this)->seekdistance = distance;
3141
// *************************************************************************
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.
3148
\sa setSeekDistance(), isSeekValueAsPercentage()
3152
So@Gui@Viewer::getSeekDistance(void) const
3154
return PRIVATE(this)->seekdistance;
3157
// *************************************************************************
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.
3164
\sa setSeekDistance(), isSeekValueAsPercentage()
3168
So@Gui@Viewer::setSeekValueAsPercentage(const SbBool on)
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",
3180
PRIVATE(this)->seekdistanceabs = on ? FALSE : TRUE;
3183
// *************************************************************************
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.
3190
\sa setSeekValuePercentage(), getSeekDistance()
3194
So@Gui@Viewer::isSeekValuePercentage(void) const
3196
return PRIVATE(this)->seekdistanceabs ? FALSE : TRUE;
3199
// ************************************************************************
3202
This method is obsoleted in Coin So@Gui@.
3205
So@Gui@Viewer::computeSeekFinalOrientation(void)
3207
SO@GUI@_OBSOLETED();
3210
// *************************************************************************
3213
If the current camera is of perspective type, switch to
3214
orthographic, and vice versa.
3216
Automatically calls So@Gui@Viewer::setCameraType() so the change
3217
will immediately take place.
3220
So@Gui@Viewer::toggleCameraType(void)
3222
SoType perspectivetype = SoPerspectiveCamera::getClassTypeId();
3223
SoType orthotype = SoOrthographicCamera::getClassTypeId();
3225
this->setCameraType(PRIVATE(this)->cameratype.isDerivedFrom(perspectivetype)
3226
? orthotype : perspectivetype);
3229
// ************************************************************************
3232
Copies the settings of \a camera into our current camera. Cameras
3233
must be of the same class type.
3236
So@Gui@Viewer::changeCameraValues(// virtual, protected
3239
assert(camera != NULL);
3241
SoCamera * cam = this->getCamera();
3243
if (SO@GUI@_DEBUG) {
3244
SoDebugError::postWarning("So@Gui@Viewer::changeCameraValues",
3245
"no current camera in the scenegraph");
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 "
3258
cam->copyFieldValues(camera, FALSE);
3261
// *************************************************************************
3265
So@Gui@Viewer::sizeChanged(const SbVec2s & size)
3267
inherited::sizeChanged(size);
3270
// *************************************************************************
3272
// Documented in superclass.
3274
So@Gui@Viewer::processSoEvent(const SoEvent * const event)
3276
const SoType type(event->getTypeId());
3277
const SoKeyboardEvent * keyevent = NULL;
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);
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
3292
if (!this->isViewing()) { return inherited::processSoEvent(event); }
3295
if (keyevent && (keyevent->getState() == SoButtonEvent::DOWN)) {
3296
switch (keyevent->getKey()) {
3297
case SoKeyboardEvent::S:
3298
this->setSeekMode(this->isSeekMode() ? FALSE : TRUE);
3300
case SoKeyboardEvent::Q:
3302
So@Gui@::exitMainLoop();
3304
case SoKeyboardEvent::HOME:
3305
this->resetToHomePosition();
3307
case SoKeyboardEvent::LEFT_ARROW:
3308
PRIVATE(this)->moveCameraScreen(SbVec2f(-0.1f, 0.0f));
3310
case SoKeyboardEvent::UP_ARROW:
3311
PRIVATE(this)->moveCameraScreen(SbVec2f(0.0f, 0.1f));
3313
case SoKeyboardEvent::RIGHT_ARROW:
3314
PRIVATE(this)->moveCameraScreen(SbVec2f(0.1f, 0.0f));
3316
case SoKeyboardEvent::DOWN_ARROW:
3317
PRIVATE(this)->moveCameraScreen(SbVec2f(0.0f, -0.1f));
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());
3332
// We got an LMB UP-event while in seek-mode, and we just
3333
// swallow the event.
3343
// *************************************************************************
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.
3350
This method is not part of the original InventorXt API.
3354
So@Gui@Viewer::addSuperimposition(SoNode * scene)
3356
if (PRIVATE(this)->superimpositions == NULL) {
3357
PRIVATE(this)->superimpositions = new SbPList;
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 "
3374
PRIVATE(this)->superimpositions->append(scene);
3375
PRIVATE(this)->superimpositionsenabled.append(TRUE);
3378
// *************************************************************************
3381
This method is not part of the original InventorXt API.
3385
So@Gui@Viewer::removeSuperimposition(SoNode * scene)
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);
3399
if (SO@GUI@_DEBUG) {
3400
SoDebugError::post("So@Gui@Viewer::removeSuperimposition",
3401
"no such superimposition");
3406
// *************************************************************************
3409
This method sets whether the superimposed scene graph should be traversed
3412
This method is not part of the original InventorXt API.
3416
So@Gui@Viewer::setSuperimpositionEnabled(SoNode * scene,
3417
const SbBool enable)
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;
3427
if (SO@GUI@_DEBUG) {
3428
SoDebugError::post("So@Gui@Viewer::setSuperimpositionEnabled",
3429
"no such superimposition");
3434
// *************************************************************************
3437
This method returns whether the superimposed scene is rendered or not.
3439
This method is not part of the original InventorXt API.
3443
So@Gui@Viewer::getSuperimpositionEnabled(SoNode * scene) const
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];
3452
if (SO@GUI@_DEBUG) {
3453
SoDebugError::post("So@Gui@Viewer::getSuperimpositionEnabled",
3454
"no such superimposition");
3459
// *************************************************************************