1// @configure_input@ 2 3/**************************************************************************\ 4 * Copyright (c) Kongsberg Oil & Gas Technologies AS 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name of the copyright holder nor the names of its 19 * contributors may be used to endorse or promote products derived from 20 * this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33\**************************************************************************/ 34 35// This file contains the generic, "templatize-able" parts of the 36// So*ExaminerViewer sourcecode. 37 38// ************************************************************************* 39 40/*! 41 \class So@Gui@ExaminerViewer So@Gui@ExaminerViewer.h Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h 42 \brief The So@Gui@ExaminerViewer class is a 3D-model examination viewer. 43 44 \ingroup components viewers 45 46 This class is the viewer considered to be the most "general purpose" 47 viewer, and it is often used in rapid prototyping to examine simple 48 models aswell as complete scenes (although for the latter, you might 49 be better off with one of the other viewer classes). 50 51 <center> 52 <img src="examinerviewer.png"> 53 </center> 54 55 Here is a complete, stand-alone example that shows how to set up an 56 So@Gui@ExaminerViewer as a model viewer that loads Inventor and VRML 57 files from disk and places them inside the viewer for the end-user 58 to examine: 59 60 \code 61 #include <Inventor/@Gui@/So@Gui@.h> 62 #include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h> 63 #include <Inventor/nodes/SoBaseColor.h> 64 #include <Inventor/nodes/SoCone.h> 65 #include <Inventor/nodes/SoSeparator.h> 66 67 int 68 main(int argc, char ** argv) 69 { 70 if (argc < 2) { 71 (void)fprintf(stderr, "\n\n\tUsage: %s <modelfilename>\n\n", 72 argc > 0 ? argv[0] : "viewerapp"); 73 exit(1); 74 } 75 76 // Initialize So@Gui@ and Inventor API libraries. This returns a main 77 // window to use. 78 @WIDGET@ mainwin = So@Gui@::init(argc, argv, argv[0]); 79 80 // Open the argument file.. 81 SoInput in; 82 SbBool ok = in.openFile(argv[1]); 83 if (!ok) { exit(1); } 84 85 // ..and import it. 86 SoSeparator * root = SoDB::readAll(&in); 87 if (root == NULL) { exit(1); } 88 root->ref(); 89 90 // Use the ExaminerViewer, for a nice interface for 3D model 91 // inspection. 92 So@Gui@ExaminerViewer * viewer = new So@Gui@ExaminerViewer(mainwin); 93 viewer->setSceneGraph(root); 94 viewer->show(); 95 96 // Pop up the main window. 97 So@Gui@::show(mainwin); 98 // Loop until exit. 99 So@Gui@::mainLoop(); 100 101 // Clean up resources. 102 delete viewer; 103 root->unref(); 104 105 return 0; 106 } 107 \endcode 108 109 So@Gui@ExaminerViewer has a convenient interface for repositioning 110 and reorientation of the camera, by panning, rotating and zooming 111 it's position. The following controls can be used: 112 113 <ul> 114 115 <li>hold down left mousebutton and move mouse pointer to rotate the 116 camera around it's current focal point (the focal point can be 117 changed by doing a seek operation)</li> 118 119 <li>hold middle mousebutton to pan (or a CTRL-key plus left 120 mousebutton, or a SHIFT-key plus left mousebutton)</li> 121 122 <li>hold down left + middle mousebutton to zoom / dolly, or CTRL + 123 middle mousebutton, or CTRL + SHIFT + the left mousebutton</li> 124 125 <li>click 's', then pick with the left mousebutton to seek</li> 126 127 <li>right mousebutton opens the popup menu</li> 128 129 <li>click 'ESC' key to switch to and from 'camera interaction' mode 130 and 'scenegraph interaction' mode (see setViewing() 131 documentation)</li> 132 133 <!-- 134 FIXME: This functionality has been disabled. See FIXME comment 135 20050202 larsa below. 136 137 <li>hold down the 'ALT' key to temporary toggle from 138 camera-interaction mode to scenegraph-interaction mode</li> 139 140 //--> 141 142 <li>'q' quits the application</li> 143 144 </ul> 145 146 The So@Gui@ExaminerViewer provides a user decoration's button for 147 toggling between orthographic or perspective camera view volumes and 148 projection methods. This is the bottom-most click button on the 149 right decoration border. 150 151 It also inherits the decoration buttons from the So@Gui@FullViewer: 152 the arrow for switching to "scenegraph interaction" mode, the hand 153 for setting back to "camera interaction" mode, the house for "reset 154 camera to home position", the blueprint house for "set new camera 155 home position", the eye for "zoom camera out to view full scene" and 156 the flashlight for setting "click to seek" mode. 157 158 Note that a common faulty assumption about all the viewer-classes is 159 that user interaction (in the "examine"-mode, not the 160 scenegraph-interaction mode) influences the model or 3D-scene in the 161 view. This is not correct, as it is always the viewer's \e camera 162 that is translated and rotated. 163 164 The initial position of the camera is placed such that all of the 165 scenegraph's geometry fits within it's view. 166 167 \sa So@Gui@FlyViewer, So@Gui@PlaneViewer 168*/ 169 170// ************************************************************************* 171 172// Documentation shared between So* toolkits follows below. 173 174/*! 175 \fn So@Gui@ExaminerViewer::So@Gui@ExaminerViewer(@WIDGET@ parent, const char * name, SbBool embed, So@Gui@FullViewer::BuildFlag flag, So@Gui@Viewer::Type type) 176 177 Constructor. See parent class for explanation of arguments. 178 Calling this constructor will make sure the examiner viewer widget 179 will be built immediately. 180*/ 181 182/*! 183 \fn So@Gui@ExaminerViewer::So@Gui@ExaminerViewer(@WIDGET@ parent, const char * name, SbBool embed, So@Gui@FullViewer::BuildFlag flag, So@Gui@Viewer::Type type, SbBool build) 184 185 Constructor. See parent class for explanation of arguments. 186*/ 187 188// ************************************************************************* 189 190#ifdef HAVE_CONFIG_H 191#include <config.h> 192#endif // HAVE_CONFIG_H 193 194#include <assert.h> 195#include <math.h> 196 197#include <Inventor/SbTime.h> 198#include <Inventor/errors/SoDebugError.h> 199#include <Inventor/nodes/SoOrthographicCamera.h> 200#include <Inventor/nodes/SoPerspectiveCamera.h> 201#include <Inventor/projectors/SbSphereSheetProjector.h> 202#include <Inventor/projectors/SbSpherePlaneProjector.h> 203#include <Inventor/events/SoKeyboardEvent.h> 204#include <Inventor/events/SoMouseButtonEvent.h> 205#include <Inventor/events/SoLocation2Event.h> 206#include <Inventor/events/SoMotion3Event.h> 207 208#include <so@gui@defs.h> 209 210#include <Inventor/@Gui@/common/gl.h> 211#include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h> 212#include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewerP.h> 213 214#include <Inventor/@Gui@/So@Gui@Basic.h> 215#include <Inventor/@Gui@/So@Gui@Cursor.h> 216#include <Inventor/@Gui@/viewers/SoGuiFullViewerP.h> // for pan() and zoom() 217 218#define PRIVATE(obj) ((obj)->pimpl) 219#define PUBLIC(obj) ((obj)->pub) 220 221static const int MOUSEPOSLOGSIZE = 16; 222 223// Bitmap representations of an "X", a "Y" and a "Z" for the axis cross. 224static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 }; 225static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 }; 226static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f }; 227 228// ************************************************************************ 229 230/*! 231 Decide if it should be possible to start a spin animation of the 232 model in the viewer by releasing the mouse button while dragging. 233 234 If the \a enable flag is \c FALSE and we're currently animating, the 235 spin will be stopped. 236 237 \sa isAnimationEnabled 238*/ 239void 240So@Gui@ExaminerViewer::setAnimationEnabled(const SbBool enable) 241{ 242 PRIVATE(this)->spinanimatingallowed = enable; 243 if (!enable && this->isAnimating()) { this->stopAnimating(); } 244} 245 246// ************************************************************************* 247 248/*! 249 Query whether or not it is possible to start a spinning animation by 250 releasing the left mouse button while dragging the mouse. 251 252 \sa setAnimationEnabled 253*/ 254 255SbBool 256So@Gui@ExaminerViewer::isAnimationEnabled(void) const 257{ 258 return PRIVATE(this)->spinanimatingallowed; 259} 260 261// ************************************************************************* 262 263/*! 264 Stop the model from spinning. 265*/ 266 267void 268So@Gui@ExaminerViewer::stopAnimating(void) 269{ 270 if (PRIVATE(this)->currentmode != SoGuiExaminerViewerP::SPINNING) { 271#if SO@GUI@_DEBUG 272 SoDebugError::postWarning("So@Gui@ExaminerViewer::stopAnimating", 273 "not animating"); 274#endif // SO@GUI@_DEBUG 275 return; 276 } 277 PRIVATE(this)->setMode(this->isViewing() ? 278 SoGuiExaminerViewerP::IDLE : 279 SoGuiExaminerViewerP::INTERACT); 280} 281 282// ************************************************************************* 283 284/*! 285 Query if the model in the viewer is currently in spinning mode after 286 a user drag. 287*/ 288 289SbBool 290So@Gui@ExaminerViewer::isAnimating(void) const 291{ 292 return PRIVATE(this)->currentmode == SoGuiExaminerViewerP::SPINNING; 293} 294 295// ************************************************************************ 296 297/*! 298 Set the flag deciding whether or not to show the axis cross. 299 300 \sa isFeedbackVisible, getFeedbackSize, setFeedbackSize 301*/ 302 303void 304So@Gui@ExaminerViewer::setFeedbackVisibility(const SbBool enable) 305{ 306 if (enable == PRIVATE(this)->axiscrossEnabled) { 307#ifdef SO@GUI@_EXTRA_DEBUG 308 SoDebugError::postWarning("So@Gui@ExaminerViewer::setFeedbackVisibility", 309 "feedback visibility already set to %s", enable ? "TRUE" : "FALSE"); 310#endif // SO@GUI@_EXTRA_DEBUG 311 return; 312 } 313 PRIVATE(this)->axiscrossEnabled = enable; 314 315 if (this->isViewing()) { this->scheduleRedraw(); } 316} 317 318/*! 319 Check if the feedback axis cross is visible. 320 321 \sa setFeedbackVisibility, getFeedbackSize, setFeedbackSize 322*/ 323 324SbBool 325So@Gui@ExaminerViewer::isFeedbackVisible(void) const 326{ 327 return PRIVATE(this)->axiscrossEnabled; 328} 329 330// ************************************************************************ 331 332/*! 333 Set the size of the feedback axiscross. The value is interpreted as 334 an approximate percentage chunk of the dimensions of the total 335 canvas. 336 337 \sa getFeedbackSize, isFeedbackVisible, setFeedbackVisibility 338*/ 339void 340So@Gui@ExaminerViewer::setFeedbackSize(const int size) 341{ 342#if SO@GUI@_DEBUG 343 if (size < 1) { 344 SoDebugError::postWarning("So@Gui@ExaminerViewer::setFeedbackSize", 345 "the size setting should be larger than 0"); 346 return; 347 } 348#endif // SO@GUI@_DEBUG 349 350 PRIVATE(this)->axiscrossSize = size; 351 352 if (this->isFeedbackVisible() && this->isViewing()) { 353 this->scheduleRedraw(); 354 } 355} 356 357/*! 358 Return the size of the feedback axis cross. Default is 25. 359 360 \sa setFeedbackSize, isFeedbackVisible, setFeedbackVisibility 361*/ 362 363int 364So@Gui@ExaminerViewer::getFeedbackSize(void) const 365{ 366 return PRIVATE(this)->axiscrossSize; 367} 368 369// ************************************************************************* 370 371// Documented in superclass. 372SbBool 373So@Gui@ExaminerViewer::processSoEvent(const SoEvent * const ev) 374{ 375#if SO@GUI@_DEBUG && 0 // debug 376 SoDebugError::postInfo("So@Gui@ExaminerViewer::processSoEvent", 377 "[invoked], event '%s'", 378 ev->getTypeId().getName().getString()); 379#endif // debug 380 381 // We're in "interact" mode (ie *not* the camera modification mode), 382 // so don't handle the event here. It should either be forwarded to 383 // the scenegraph, or caught by So@Gui@Viewer::processSoEvent() if 384 // it's an ESC or ALT press (to switch modes). 385 if (!this->isViewing()) { return inherited::processSoEvent(ev); } 386 387 // Events when in "ready-to-seek" mode are ignored, except those 388 // which influence the seek mode itself -- these are handled further 389 // up the inheritance hierarchy. 390 if (this->isSeekMode()) { return inherited::processSoEvent(ev); } 391 392 const SoType type(ev->getTypeId()); 393 394 const SbVec2s size(this->getGLSize()); 395 const SbVec2f prevnormalized = PRIVATE(this)->lastmouseposition; 396 const SbVec2s pos(ev->getPosition()); 397 const SbVec2f posn((float) pos[0] / (float) So@Gui@Max((int)(size[0] - 1), 1), 398 (float) pos[1] / (float) So@Gui@Max((int)(size[1] - 1), 1)); 399 400 PRIVATE(this)->lastmouseposition = posn; 401 402 // Set to TRUE if any event processing happened. Note that it is not 403 // necessary to restrict ourselves to only do one "action" for an 404 // event, we only need this flag to see if any processing happened 405 // at all. 406 SbBool processed = FALSE; 407 408 const SoGuiExaminerViewerP::ViewerMode currentmode = PRIVATE(this)->currentmode; 409 SoGuiExaminerViewerP::ViewerMode newmode = currentmode; 410 411 PRIVATE(this)->ctrldown = ev->wasCtrlDown(); 412 PRIVATE(this)->shiftdown = ev->wasShiftDown(); 413 414 // Mouse Button / Spaceball Button handling 415 416 if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { 417 processed = TRUE; 418 419 const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; 420 const int button = event->getButton(); 421 const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; 422 423 switch (button) { 424 case SoMouseButtonEvent::BUTTON1: 425 PRIVATE(this)->button1down = press; 426 if (press && (currentmode == SoGuiExaminerViewerP::SEEK_WAIT_MODE)) { 427 newmode = SoGuiExaminerViewerP::SEEK_MODE; 428 this->seekToPoint(pos); // implicitly calls interactiveCountInc() 429 } 430 break; 431 case SoMouseButtonEvent::BUTTON2: 432 processed = FALSE; // pass on to superclass, so popup menu is shown 433 break; 434 case SoMouseButtonEvent::BUTTON3: 435 PRIVATE(this)->button3down = press; 436 break; 437#ifdef HAVE_SOMOUSEBUTTONEVENT_BUTTON5 438 case SoMouseButtonEvent::BUTTON4: 439 if (press) SoGuiFullViewerP::zoom(this->getCamera(), 0.1f); 440 break; 441 case SoMouseButtonEvent::BUTTON5: 442 if (press) SoGuiFullViewerP::zoom(this->getCamera(), -0.1f); 443 break; 444#endif // HAVE_SOMOUSEBUTTONEVENT_BUTTON5 445 default: 446 break; 447 } 448 } 449 450 // Keyboard handling 451 if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { 452 const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; 453 const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; 454 switch (event->getKey()) { 455 case SoKeyboardEvent::LEFT_CONTROL: 456 case SoKeyboardEvent::RIGHT_CONTROL: 457 processed = TRUE; 458 PRIVATE(this)->ctrldown = press; 459 break; 460 case SoKeyboardEvent::LEFT_SHIFT: 461 case SoKeyboardEvent::RIGHT_SHIFT: 462 processed = TRUE; 463 PRIVATE(this)->shiftdown = press; 464 break; 465 default: 466 break; 467 } 468 } 469 470 // Mouse Movement handling 471 if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) { 472 const SoLocation2Event * const event = (const SoLocation2Event *) ev; 473 474 processed = TRUE; 475 476 if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::ZOOMING) { 477 PRIVATE(this)->zoomByCursor(posn, prevnormalized); 478 } 479 else if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::PANNING) { 480 SoGuiFullViewerP::pan(this->getCamera(), this->getGLAspectRatio(), 481 PRIVATE(this)->panningplane, posn, prevnormalized); 482 } 483 else if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::DRAGGING) { 484 PRIVATE(this)->addToLog(event->getPosition(), event->getTime()); 485 PRIVATE(this)->spin(posn); 486 } 487 else { 488 processed = FALSE; 489 } 490 } 491 492 // Spaceball & Joystick handling 493 if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) { 494 SoMotion3Event * const event = (SoMotion3Event *) ev; 495 SoCamera * const camera = this->getCamera(); 496 if (camera) { 497 if (PRIVATE(this)->motion3OnCamera) { 498 SbVec3f dir = event->getTranslation(); 499 camera->orientation.getValue().multVec(dir,dir); 500 camera->position = camera->position.getValue() + dir; 501 camera->orientation = 502 event->getRotation() * camera->orientation.getValue(); 503 processed = TRUE; 504 } 505 else { 506 // FIXME: move/rotate model 507#if SO@GUI@_DEBUG 508 SoDebugError::postInfo("So@Gui@ExaminerViewer::processSoEvent", 509 "SoMotion3Event for model movement is not implemented yet"); 510#endif // SO@GUI@_DEBUG 511 processed = TRUE; 512 } 513 } 514 } 515 516 enum { 517 BUTTON1DOWN = 1 << 0, 518 BUTTON3DOWN = 1 << 1, 519 CTRLDOWN = 1 << 2, 520 SHIFTDOWN = 1 << 3 521 }; 522 unsigned int combo = 523 (PRIVATE(this)->button1down ? BUTTON1DOWN : 0) | 524 (PRIVATE(this)->button3down ? BUTTON3DOWN : 0) | 525 (PRIVATE(this)->ctrldown ? CTRLDOWN : 0) | 526 (PRIVATE(this)->shiftdown ? SHIFTDOWN : 0); 527 528 switch (combo) { 529 case 0: 530 if (currentmode == SoGuiExaminerViewerP::SPINNING) { break; } 531 newmode = SoGuiExaminerViewerP::IDLE; 532 if ((currentmode == SoGuiExaminerViewerP::DRAGGING) && 533 this->isAnimationEnabled() && (PRIVATE(this)->log.historysize >= 3)) { 534 SbTime stoptime = (ev->getTime() - PRIVATE(this)->log.time[0]); 535 if (stoptime.getValue() < 0.100) { 536 const SbVec2s glsize(this->getGLSize()); 537 SbVec3f from = PRIVATE(this)->spinprojector->project(SbVec2f(float(PRIVATE(this)->log.position[2][0]) / float(So@Gui@Max(glsize[0]-1, 1)), 538 float(PRIVATE(this)->log.position[2][1]) / float(So@Gui@Max(glsize[1]-1, 1)))); 539 SbVec3f to = PRIVATE(this)->spinprojector->project(posn); 540 SbRotation rot = PRIVATE(this)->spinprojector->getRotation(from, to); 541 542 SbTime delta = (PRIVATE(this)->log.time[0] - PRIVATE(this)->log.time[2]); 543 double deltatime = delta.getValue(); 544 rot.invert(); 545 rot.scaleAngle(float(0.200 / deltatime)); 546 547 SbVec3f axis; 548 float radians; 549 rot.getValue(axis, radians); 550 if ((radians > 0.01f) && (deltatime < 0.300)) { 551 newmode = SoGuiExaminerViewerP::SPINNING; 552 PRIVATE(this)->spinRotation = rot; 553 } 554 } 555 } 556 break; 557 case BUTTON1DOWN: 558 newmode = SoGuiExaminerViewerP::DRAGGING; 559 break; 560 case BUTTON3DOWN: 561 case CTRLDOWN|BUTTON1DOWN: 562 case SHIFTDOWN|BUTTON1DOWN: 563 newmode = SoGuiExaminerViewerP::PANNING; 564 break; 565 case BUTTON1DOWN|BUTTON3DOWN: 566 case CTRLDOWN|BUTTON3DOWN: 567 case CTRLDOWN|SHIFTDOWN|BUTTON1DOWN: 568 newmode = SoGuiExaminerViewerP::ZOOMING; 569 break; 570 571 // There are many cases we don't handle that just falls through to 572 // the default case, like SHIFTDOWN, CTRLDOWN, CTRLDOWN|SHIFTDOWN, 573 // SHIFTDOWN|BUTTON3DOWN, SHIFTDOWN|CTRLDOWN|BUTTON3DOWN, etc. 574 // This is a feature, not a bug. :-) 575 // 576 // mortene. 577 578 default: 579 // The default will make a spin stop and otherwise not do 580 // anything. 581 if ((currentmode != SoGuiExaminerViewerP::SEEK_WAIT_MODE) && 582 (currentmode != SoGuiExaminerViewerP::SEEK_MODE)) { 583 newmode = SoGuiExaminerViewerP::IDLE; 584 } 585 break; 586 } 587 588 if (newmode != currentmode) { 589 PRIVATE(this)->setMode(newmode); 590 } 591 592 // If not handled in this class, pass on upwards in the inheritance 593 // hierarchy. 594 return processed || inherited::processSoEvent(ev); 595} 596 597// ************************************************************************* 598 599// documented in superclass 600void 601So@Gui@ExaminerViewer::setSeekMode(SbBool on) 602{ 603 // Overrides this method to make sure any animations are stopped 604 // before we go into seek mode. 605 606 // Note: this method is almost identical to the setSeekMode() in the 607 // So@Gui@FlyViewer and So@Gui@PlaneViewer, so migrate any changes. 608 609#if SO@GUI@_DEBUG 610 if (on == this->isSeekMode()) { 611 SoDebugError::postWarning("So@Gui@ExaminerViewer::setSeekMode", 612 "seek mode already %sset", on ? "" : "un"); 613 return; 614 } 615#endif // SO@GUI@_DEBUG 616 617 if (this->isAnimating()) { this->stopAnimating(); } 618 inherited::setSeekMode(on); 619 PRIVATE(this)->setMode(on ? 620 SoGuiExaminerViewerP::SEEK_WAIT_MODE : 621 (this->isViewing() ? 622 SoGuiExaminerViewerP::IDLE : SoGuiExaminerViewerP::INTERACT)); 623} 624 625// ************************************************************************* 626 627/*! 628 Decide whether or not the mouse pointer cursor should be visible in 629 the rendering canvas. 630*/ 631void 632So@Gui@ExaminerViewer::setCursorEnabled(SbBool enable) 633{ 634 inherited::setCursorEnabled(enable); 635 PRIVATE(this)->setCursorRepresentation(PRIVATE(this)->currentmode); 636} 637 638// ************************************************************************* 639 640// Documented in superclass. 641const char * 642So@Gui@ExaminerViewer::getDefaultWidgetName(void) const 643{ 644 return "So@Gui@ExaminerViewer"; 645} 646 647// ************************************************************************* 648 649// Documented in superclass. 650const char * 651So@Gui@ExaminerViewer::getDefaultTitle(void) const 652{ 653 return "Examiner Viewer"; 654} 655 656// ************************************************************************* 657 658// Documented in superclass. 659const char * 660So@Gui@ExaminerViewer::getDefaultIconTitle(void) const 661{ 662 return "Examiner Viewer"; 663} 664 665// ************************************************************************* 666 667// Documented in superclass. Overrides this method to be able to draw 668// the axis cross, if selected, and to keep a continuous animation 669// upon spin. 670void 671So@Gui@ExaminerViewer::actualRedraw(void) 672{ 673 SbTime now = SbTime::getTimeOfDay(); 674 double secs = now.getValue() - PRIVATE(this)->prevRedrawTime.getValue(); 675 676 PRIVATE(this)->prevRedrawTime = now; 677 678 if (this->isAnimating()) { 679 SbRotation deltaRotation = PRIVATE(this)->spinRotation; 680 deltaRotation.scaleAngle(float(secs * 5.0)); 681 PRIVATE(this)->reorientCamera(deltaRotation); 682 } 683 684 inherited::actualRedraw(); 685 686 if (this->isFeedbackVisible()) { PRIVATE(this)->drawAxisCross(); } 687 688 // Immediately reschedule to get continous spin animation. 689 if (this->isAnimating()) { this->scheduleRedraw(); } 690} 691 692// ************************************************************************* 693 694// doc in super 695void 696So@Gui@ExaminerViewer::afterRealizeHook(void) 697{ 698 inherited::afterRealizeHook(); 699 PRIVATE(this)->setCursorRepresentation(PRIVATE(this)->currentmode); 700} 701 702// ************************************************************************* 703 704// Documented in superclass. Overridden to provide the examiner viewer 705// functionality on the left thumbwheel (x axis rotation). 706void 707So@Gui@ExaminerViewer::leftWheelMotion(float value) 708{ 709 if (this->isAnimating()) this->stopAnimating(); 710 711 float newval = PRIVATE(this)->rotXWheelMotion(value, this->getLeftWheelValue()); 712 inherited::leftWheelMotion(newval); 713} 714 715// Documented in superclass. Overridden to provide the examiner viewer 716// functionality on the bottom thumbwheel (y axis rotation). 717void 718So@Gui@ExaminerViewer::bottomWheelMotion(float value) 719{ 720 if (this->isAnimating()) this->stopAnimating(); 721 722 float newval = PRIVATE(this)->rotYWheelMotion(value, this->getBottomWheelValue()); 723 inherited::bottomWheelMotion(newval); 724} 725 726// Documented in superclass. Overridden to provide the examiner viewer 727// functionality on the left thumbwheel (dolly/zoom). 728void 729So@Gui@ExaminerViewer::rightWheelMotion(float value) 730{ 731 SoGuiFullViewerP::zoom(this->getCamera(), this->getRightWheelValue() - value); 732 inherited::rightWheelMotion(value); 733} 734 735// ************************************************************************* 736 737// Documented in superclass. This method overridden from parent class 738// to make sure the mouse pointer cursor is updated. 739void 740So@Gui@ExaminerViewer::setViewing(SbBool enable) 741{ 742 if (!!this->isViewing() == !!enable) { 743#if SO@GUI@_DEBUG 744 SoDebugError::postWarning("So@Gui@ExaminerViewer::setViewing", 745 "current state already %s", enable ? "TRUE" : "FALSE"); 746#endif // SO@GUI@_DEBUG 747 return; 748 } 749 750 PRIVATE(this)->setMode(enable ? 751 SoGuiExaminerViewerP::IDLE : 752 SoGuiExaminerViewerP::INTERACT); 753 inherited::setViewing(enable); 754} 755 756// ************************************************************************* 757 758#ifndef DOXYGEN_SKIP_THIS 759 760// Remaining code is for the SoGuiExaminerViewerP "private 761// implementation" class. 762 763SoGuiExaminerViewerP::SoGuiExaminerViewerP(So@Gui@ExaminerViewer * publ) 764{ 765 PUBLIC(this) = publ; 766} 767 768SoGuiExaminerViewerP::~SoGuiExaminerViewerP() 769{ 770} 771 772void 773SoGuiExaminerViewerP::genericConstructor(void) 774{ 775 this->currentmode = SoGuiExaminerViewerP::IDLE; 776 777 this->prevRedrawTime = SbTime::getTimeOfDay(); 778 this->spinanimatingallowed = TRUE; 779 this->spinsamplecounter = 0; 780 this->spinincrement = SbRotation::identity(); 781 782 // FIXME: use a smaller sphere than the default one to have a larger 783 // area close to the borders that gives us "z-axis rotation"? 784 // 19990425 mortene. 785 this->spinprojector = new SbSphereSheetProjector(SbSphere(SbVec3f(0, 0, 0), 0.8f)); 786 SbViewVolume volume; 787 volume.ortho(-1, 1, -1, 1, -1, 1); 788 this->spinprojector->setViewVolume(volume); 789 790 this->axiscrossEnabled = FALSE; 791 this->axiscrossSize = 25; 792 793 this->spinRotation.setValue(SbVec3f(0, 0, -1), 0); 794 795 this->log.size = MOUSEPOSLOGSIZE; 796 this->log.position = new SbVec2s [ MOUSEPOSLOGSIZE ]; 797 this->log.time = new SbTime [ MOUSEPOSLOGSIZE ]; 798 this->log.historysize = 0; 799 this->button1down = FALSE; 800 this->button3down = FALSE; 801 this->ctrldown = FALSE; 802 this->shiftdown = FALSE; 803 this->pointer.now = SbVec2s(0, 0); 804 this->pointer.then = SbVec2s(0, 0); 805 this->motion3OnCamera = TRUE; 806} 807 808void 809SoGuiExaminerViewerP::genericDestructor(void) 810{ 811 delete this->spinprojector; 812 delete[] this->log.position; 813 delete[] this->log.time; 814} 815 816// ************************************************************************ 817 818// rotate a camera around its focalpoint, in the direction around the 819// given axis, by the given delta value (in radians) 820void 821SoGuiExaminerViewerP::rotateCamera(SoCamera * cam, 822 const SbVec3f & aroundaxis, 823 const float delta) 824{ 825 const SbVec3f DEFAULTDIRECTION(0, 0, -1); 826 const SbRotation currentorientation = cam->orientation.getValue(); 827 828 SbVec3f currentdir; 829 currentorientation.multVec(DEFAULTDIRECTION, currentdir); 830 831 const SbVec3f focalpoint = cam->position.getValue() + 832 cam->focalDistance.getValue() * currentdir; 833 834 // set new orientation 835 cam->orientation = SbRotation(aroundaxis, delta) * currentorientation; 836 837 SbVec3f newdir; 838 cam->orientation.getValue().multVec(DEFAULTDIRECTION, newdir); 839 cam->position = focalpoint - cam->focalDistance.getValue() * newdir; 840} 841 842// The "rotX" wheel is the wheel on the left decoration on the 843// examiner viewer. This function translates interaction with the 844// "rotX" wheel into camera movement. 845float 846SoGuiExaminerViewerP::rotXWheelMotion(float value, float oldvalue) 847{ 848 SoCamera * cam = PUBLIC(this)->getCamera(); 849 if (cam == NULL) return 0.0f; // can happen for empty scenegraph 850 851 SoGuiExaminerViewerP::rotateCamera(cam, SbVec3f(-1, 0, 0), value - oldvalue); 852 return value; 853} 854 855// The "rotY" wheel is the wheel on the bottom decoration on the 856// examiner viewer. This function translates interaction with the 857// "rotX" wheel into camera movement. 858float 859SoGuiExaminerViewerP::rotYWheelMotion(float value, float oldvalue) 860{ 861 SoCamera * cam = PUBLIC(this)->getCamera(); 862 if (cam == NULL) return 0.0f; // can happen for empty scenegraph 863 864 SoGuiExaminerViewerP::rotateCamera(cam, SbVec3f(0, -1, 0), value - oldvalue); 865 return value; 866} 867 868// ************************************************************************ 869 870// The viewer is a state machine, and all changes to the current state 871// are made through this call. 872void 873SoGuiExaminerViewerP::setMode(const ViewerMode newmode) 874{ 875 const ViewerMode oldmode = this->currentmode; 876 if (newmode == oldmode) { return; } 877 878 switch (newmode) { 879 case DRAGGING: 880 // Set up initial projection point for the projector object when 881 // first starting a drag operation. 882 this->spinprojector->project(this->lastmouseposition); 883 PUBLIC(this)->interactiveCountInc(); 884 this->clearLog(); 885 break; 886 887 case SPINNING: 888 PUBLIC(this)->interactiveCountInc(); 889 PUBLIC(this)->scheduleRedraw(); 890 break; 891 892 case PANNING: 893 { 894 // The plane we're projecting the mouse coordinates to get 3D 895 // coordinates should stay the same during the whole pan 896 // operation, so we should calculate this value here. 897 SoCamera * cam = PUBLIC(this)->getCamera(); 898 if (cam == NULL) { // can happen for empty scenegraph 899 this->panningplane = SbPlane(SbVec3f(0, 0, 1), 0); 900 } 901 else { 902 SbViewVolume vv = cam->getViewVolume(PUBLIC(this)->getGLAspectRatio()); 903 this->panningplane = vv.getPlane(cam->focalDistance.getValue()); 904 } 905 } 906 PUBLIC(this)->interactiveCountInc(); 907 break; 908 909 case ZOOMING: 910 PUBLIC(this)->interactiveCountInc(); 911 break; 912 913 default: // include default to avoid compiler warnings. 914 break; 915 } 916 917 switch (oldmode) { 918 case SPINNING: 919 case DRAGGING: 920 case PANNING: 921 case ZOOMING: 922 PUBLIC(this)->interactiveCountDec(); 923 break; 924 925 default: 926 break; 927 } 928 929#if SO@GUI@_DEBUG && 0 // debug 930 if (oldmode == ZOOMING) { 931 SbVec3f v = PUBLIC(this)->getCamera()->position.getValue(); 932 SoDebugError::postInfo("So@Gui@ExaminerViewerP::setMode", 933 "new camera position after zoom: <%e, %e, %e>", 934 v[0], v[1], v[2]); 935 } 936#endif // debug 937 938 this->setCursorRepresentation(newmode); 939 this->currentmode = newmode; 940} 941 942// ************************************************************************ 943 944void 945SoGuiExaminerViewerP::drawAxisCross(void) 946{ 947 // FIXME: convert this to a superimposition scenegraph instead of 948 // OpenGL calls. 20020603 mortene. 949 950 // Store GL state. 951 glPushAttrib(GL_ALL_ATTRIB_BITS); 952 GLfloat depthrange[2]; 953 glGetFloatv(GL_DEPTH_RANGE, depthrange); 954 GLdouble projectionmatrix[16]; 955 glGetDoublev(GL_PROJECTION_MATRIX, projectionmatrix); 956 957 glDepthFunc(GL_ALWAYS); 958 glDepthMask(GL_TRUE); 959 glDepthRange(0, 0); 960 glEnable(GL_DEPTH_TEST); 961 glDisable(GL_LIGHTING); 962 glEnable(GL_COLOR_MATERIAL); 963 glDisable(GL_BLEND); // Kills transparency. 964 965 // Set the viewport in the OpenGL canvas. Dimensions are calculated 966 // as a percentage of the total canvas size. 967 SbVec2s view = PUBLIC(this)->getGLSize(); 968 const int pixelarea = 969 int(float(this->axiscrossSize)/100.0f * So@Gui@Min(view[0], view[1])); 970#if 0 // middle of canvas 971 SbVec2s origin(view[0]/2 - pixelarea/2, view[1]/2 - pixelarea/2); 972#endif // middle of canvas 973#if 1 // lower right of canvas 974 SbVec2s origin(view[0] - pixelarea, 0); 975#endif // lower right of canvas 976 glViewport(origin[0], origin[1], pixelarea, pixelarea); 977 978 979 980 // Set up the projection matrix. 981 glMatrixMode(GL_PROJECTION); 982 glLoadIdentity(); 983 984 const float NEARVAL = 0.1f; 985 const float FARVAL = 10.0f; 986 const float dim = NEARVAL * float(tan(M_PI / 8.0)); // FOV is 45� (45/360 = 1/8) 987 glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL); 988 989 990 // Set up the model matrix. 991 glMatrixMode(GL_MODELVIEW); 992 glPushMatrix(); 993 SbMatrix mx; 994 SoCamera * cam = PUBLIC(this)->getCamera(); 995 996 // If there is no camera (like for an empty scene, for instance), 997 // just use an identity rotation. 998 if (cam) { mx = cam->orientation.getValue(); } 999 else { mx = SbMatrix::identity(); } 1000 1001 mx = mx.inverse(); 1002 mx[3][2] = -3.5; // Translate away from the projection point (along z axis). 1003 glLoadMatrixf((float *)mx); 1004 1005 1006 // Find unit vector end points. 1007 SbMatrix px; 1008 glGetFloatv(GL_PROJECTION_MATRIX, (float *)px); 1009 SbMatrix comb = mx.multRight(px); 1010 1011 SbVec3f xpos; 1012 comb.multVecMatrix(SbVec3f(1,0,0), xpos); 1013 xpos[0] = (1 + xpos[0]) * view[0]/2; 1014 xpos[1] = (1 + xpos[1]) * view[1]/2; 1015 SbVec3f ypos; 1016 comb.multVecMatrix(SbVec3f(0,1,0), ypos); 1017 ypos[0] = (1 + ypos[0]) * view[0]/2; 1018 ypos[1] = (1 + ypos[1]) * view[1]/2; 1019 SbVec3f zpos; 1020 comb.multVecMatrix(SbVec3f(0,0,1), zpos); 1021 zpos[0] = (1 + zpos[0]) * view[0]/2; 1022 zpos[1] = (1 + zpos[1]) * view[1]/2; 1023 1024 1025 // Render the cross. 1026 { 1027 glLineWidth(2.0); 1028 1029 enum { XAXIS, YAXIS, ZAXIS }; 1030 int idx[3] = { XAXIS, YAXIS, ZAXIS }; 1031 float val[3] = { xpos[2], ypos[2], zpos[2] }; 1032 1033 // Bubble sort.. :-} 1034 if (val[0] < val[1]) { So@Gui@Swap(val[0], val[1]); So@Gui@Swap(idx[0], idx[1]); } 1035 if (val[1] < val[2]) { So@Gui@Swap(val[1], val[2]); So@Gui@Swap(idx[1], idx[2]); } 1036 if (val[0] < val[1]) { So@Gui@Swap(val[0], val[1]); So@Gui@Swap(idx[0], idx[1]); } 1037 assert((val[0] >= val[1]) && (val[1] >= val[2])); // Just checking.. 1038 1039 for (int i=0; i < 3; i++) { 1040 glPushMatrix(); 1041 if (idx[i] == XAXIS) { // X axis. 1042 glColor3f(0.500f, 0.125f, 0.125f); 1043 } else if (idx[i] == YAXIS) { // Y axis. 1044 glRotatef(90, 0, 0, 1); 1045 glColor3f(0.125f, 0.500f, 0.125f); 1046 } else { // Z axis. 1047 glRotatef(-90, 0, 1, 0); 1048 glColor3f(0.125f, 0.125f, 0.500f); 1049 } 1050 this->drawArrow(); 1051 glPopMatrix(); 1052 } 1053 } 1054 1055 // Render axis notation letters ("X", "Y", "Z"). 1056 glMatrixMode(GL_PROJECTION); 1057 glLoadIdentity(); 1058 glOrtho(0, view[0], 0, view[1], -1, 1); 1059 1060 glMatrixMode(GL_MODELVIEW); 1061 glLoadIdentity(); 1062 1063 GLint unpack; 1064 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack); 1065 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1066 1067 glColor3fv(SbVec3f(0.8f, 0.8f, 0.0f).getValue()); 1068 1069 glRasterPos2d(xpos[0], xpos[1]); 1070 glBitmap(8, 7, 0, 0, 0, 0, xbmp); 1071 glRasterPos2d(ypos[0], ypos[1]); 1072 glBitmap(8, 7, 0, 0, 0, 0, ybmp); 1073 glRasterPos2d(zpos[0], zpos[1]); 1074 glBitmap(8, 7, 0, 0, 0, 0, zbmp); 1075 1076 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); 1077 glPopMatrix(); 1078 1079 // Reset original state. 1080 1081 // FIXME: are these 3 lines really necessary, as we push 1082 // GL_ALL_ATTRIB_BITS at the start? 20000604 mortene. 1083 glDepthRange(depthrange[0], depthrange[1]); 1084 glMatrixMode(GL_PROJECTION); 1085 glLoadMatrixd(projectionmatrix); 1086 1087 glPopAttrib(); 1088} 1089 1090// Draw an arrow for the axis representation directly through OpenGL. 1091void 1092SoGuiExaminerViewerP::drawArrow(void) 1093{ 1094 glBegin(GL_LINES); 1095 glVertex3f(0.0f, 0.0f, 0.0f); 1096 glVertex3f(1.0f, 0.0f, 0.0f); 1097 glEnd(); 1098 glDisable(GL_CULL_FACE); 1099 glBegin(GL_TRIANGLES); 1100 glVertex3f(1.0f, 0.0f, 0.0f); 1101 glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f); 1102 glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f); 1103 glVertex3f(1.0f, 0.0f, 0.0f); 1104 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f); 1105 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f); 1106 glEnd(); 1107 glBegin(GL_QUADS); 1108 glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f); 1109 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f); 1110 glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f); 1111 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f); 1112 glEnd(); 1113} 1114 1115// ************************************************************************ 1116 1117// Rotate the camera by the given amount, then reposition it so we're 1118// still pointing at the same focal point. 1119void 1120SoGuiExaminerViewerP::reorientCamera(const SbRotation & rot) 1121{ 1122 SoCamera * cam = PUBLIC(this)->getCamera(); 1123 if (cam == NULL) return; 1124 1125 // Find global coordinates of focal point. 1126 SbVec3f direction; 1127 cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); 1128 SbVec3f focalpoint = cam->position.getValue() + 1129 cam->focalDistance.getValue() * direction; 1130 1131 // Set new orientation value by accumulating the new rotation. 1132 cam->orientation = rot * cam->orientation.getValue(); 1133 1134 // Reposition camera so we are still pointing at the same old focal point. 1135 cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); 1136 cam->position = focalpoint - cam->focalDistance.getValue() * direction; 1137} 1138 1139// ************************************************************************ 1140 1141// Uses the sphere sheet projector to map the mouseposition unto 1142// a 3D point and find a rotation from this and the last calculated point. 1143void 1144SoGuiExaminerViewerP::spin(const SbVec2f & pointerpos) 1145{ 1146 if (this->log.historysize < 2) return; 1147 assert(this->spinprojector != NULL); 1148 1149 SbVec2s glsize(PUBLIC(this)->getGLSize()); 1150 SbVec2f lastpos; 1151 lastpos[0] = float(this->log.position[1][0]) / float(So@Gui@Max((int)(glsize[0]-1), 1)); 1152 lastpos[1] = float(this->log.position[1][1]) / float(So@Gui@Max((int)(glsize[1]-1), 1)); 1153 1154 this->spinprojector->project(lastpos); 1155 SbRotation r; 1156 this->spinprojector->projectAndGetRotation(pointerpos, r); 1157 r.invert(); 1158 this->reorientCamera(r); 1159 1160 // Calculate an average angle magnitude value to make the transition 1161 // to a possible spin animation mode appear smooth. 1162 1163 SbVec3f dummy_axis, newaxis; 1164 float acc_angle, newangle; 1165 this->spinincrement.getValue(dummy_axis, acc_angle); 1166 acc_angle *= this->spinsamplecounter; // weight 1167 r.getValue(newaxis, newangle); 1168 acc_angle += newangle; 1169 1170 this->spinsamplecounter++; 1171 acc_angle /= this->spinsamplecounter; 1172 // FIXME: accumulate and average axis vectors aswell? 19990501 mortene. 1173 this->spinincrement.setValue(newaxis, acc_angle); 1174 1175 // Don't carry too much baggage, as that'll give unwanted results 1176 // when the user quickly trigger (as in "click-drag-release") a spin 1177 // animation. 1178 if (this->spinsamplecounter > 3) this->spinsamplecounter = 3; 1179} 1180 1181// ************************************************************************ 1182 1183// Calculate a zoom/dolly factor from the difference of the current 1184// cursor position and the last. 1185void 1186SoGuiExaminerViewerP::zoomByCursor(const SbVec2f & thispos, 1187 const SbVec2f & prevpos) 1188{ 1189 // There is no "geometrically correct" value, 20 just seems to give 1190 // about the right "feel". 1191 SoGuiFullViewerP::zoom(PUBLIC(this)->getCamera(), 1192 (thispos[1] - prevpos[1]) * 20.0f); 1193} 1194 1195// ************************************************************************* 1196// Methods used for spin animation tracking. 1197 1198// This method "clears" the mouse location log, used for spin 1199// animation calculations. 1200void 1201SoGuiExaminerViewerP::clearLog(void) 1202{ 1203 this->log.historysize = 0; 1204} 1205 1206// This method adds another point to the mouse location log, used for spin 1207// animation calculations. 1208void 1209SoGuiExaminerViewerP::addToLog(const SbVec2s pos, const SbTime time) 1210{ 1211 // In case someone changes the const size setting at the top of this 1212 // file too small. 1213 assert (this->log.size > 2 && "mouse log too small!"); 1214 1215 if (this->log.historysize > 0 && pos == this->log.position[0]) { 1216#if SO@GUI@_DEBUG && 0 // debug 1217 // This can at least happen under SoQt. 1218 SoDebugError::postInfo("SoGuiExaminerViewerP::addToLog", "got position already!"); 1219#endif // debug 1220 return; 1221 } 1222 1223 int lastidx = this->log.historysize; 1224 // If we've filled up the log, we should throw away the last item: 1225 if (lastidx == this->log.size) { lastidx--; } 1226 1227 assert(lastidx < this->log.size); 1228 for (int i = lastidx; i > 0; i--) { 1229 this->log.position[i] = this->log.position[i-1]; 1230 this->log.time[i] = this->log.time[i-1]; 1231 } 1232 1233 this->log.position[0] = pos; 1234 this->log.time[0] = time; 1235 if (this->log.historysize < this->log.size) 1236 this->log.historysize += 1; 1237} 1238 1239// ************************************************************************* 1240 1241// This method sets whether Motion3 events should affect the camera or 1242// the model. 1243void 1244SoGuiExaminerViewerP::setMotion3OnCamera(SbBool enable) 1245{ 1246 this->motion3OnCamera = enable; 1247} 1248 1249// This method returns whether Motion3 events affects the camera or 1250// the model. 1251SbBool 1252SoGuiExaminerViewerP::getMotion3OnCamera(void) const 1253{ 1254 return this->motion3OnCamera; 1255} 1256 1257// ************************************************************************ 1258 1259// Set cursor graphics according to mode. 1260void 1261SoGuiExaminerViewerP::setCursorRepresentation(int modearg) 1262{ 1263 if (!PUBLIC(this)->isCursorEnabled()) { 1264 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getBlankCursor()); 1265 return; 1266 } 1267 1268 switch (modearg) { 1269 case SoGuiExaminerViewerP::INTERACT: 1270 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::DEFAULT)); 1271 break; 1272 1273 case SoGuiExaminerViewerP::IDLE: 1274 case SoGuiExaminerViewerP::DRAGGING: 1275 case SoGuiExaminerViewerP::SPINNING: 1276 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getRotateCursor()); 1277 break; 1278 1279 case SoGuiExaminerViewerP::ZOOMING: 1280 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getZoomCursor()); 1281 break; 1282 1283 case SoGuiExaminerViewerP::SEEK_MODE: 1284 case SoGuiExaminerViewerP::SEEK_WAIT_MODE: 1285 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::CROSSHAIR)); 1286 break; 1287 1288 case SoGuiExaminerViewerP::PANNING: 1289 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getPanCursor()); 1290 break; 1291 1292 default: assert(0); break; 1293 } 1294} 1295 1296#endif // DOXYGEN_SKIP_THIS 1297 1298// ************************************************************************* 1299 1300#undef PRIVATE 1301#undef PUBLIC 1302 1303