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/*! 36 \class So@Gui@FlyViewer Inventor/@Gui@/viewers/So@Gui@FlyViewer.h 37 \brief The So@Gui@FlyViewer class implements controls for moving 38 the camera in a "flying" motion. 39 40 \ingroup viewers 41 42 Controls: 43 <ul> 44 45 <li>Left mouse button increases the speed.</li> 46 47 <li>Middle mouse button decreases the speed.</li> 48 49 <li>Left and middle mouse button together sets the speed to zero.</li> 50 51 <li>"s" puts the viewer in seek mode. Click some geometry with the 52 left mouse button to start the seek zoom animation. (Hitting "s" 53 again before clicking will cancel the seek operation.)</li> 54 55 <li>"u" puts the viewer in up-vector pick mode. Click some geometry 56 with the left mouse button to set the camera's up-vector to the 57 normal vector of the face you pick. 58 (Hitting "u" again before clicking will cancel the pick operation.)</li> 59 60 <li>The control key stops the flying and lets you tilt the camera by moving 61 the pointer.</li> 62 63 </ul> 64*/ 65 66/* 67 FIXME: 68 - animate camera when setting up-vector so the scene doesn't just 69 suddenly change. 70*/ 71 72#include <so@gui@defs.h> 73#include <Inventor/@Gui@/viewers/So@Gui@FlyViewer.h> 74#include <Inventor/events/SoKeyboardEvent.h> 75#include <Inventor/events/SoLocation2Event.h> 76#include <Inventor/events/SoMouseButtonEvent.h> 77#include <Inventor/nodes/SoCamera.h> 78#include <Inventor/nodes/SoCallback.h> 79#include <Inventor/nodes/SoCoordinate3.h> 80#include <Inventor/nodes/SoSwitch.h> 81#include <Inventor/nodes/SoScale.h> 82#include <Inventor/nodes/SoTranslation.h> 83#include <Inventor/errors/SoDebugError.h> 84#include <Inventor/actions/SoSearchAction.h> 85#include <Inventor/actions/SoHandleEventAction.h> 86#include <Inventor/actions/SoGetBoundingBoxAction.h> 87#include <string.h> // strlen() etc 88#include <stdlib.h> // abs() 89#include <Inventor/@Gui@/So@Gui@Cursor.h> 90 91 92// ************************************************************************ 93 94#ifndef DOXYGEN_SKIP_THIS 95 96// FIXME: We should probably move this class out of this 97// impl-file. There is code here that could be factored out and reused 98// in other modules, for example camera handling. Now there is a 99// duplication with some of the code in So@Gui@ConstrainedViewerP, for 100// example. 20021017 rolvs 101class So@Gui@FlyViewerP { 102public: 103 So@Gui@FlyViewerP(So@Gui@FlyViewer * owner); 104 ~So@Gui@FlyViewerP(); 105 106 enum ViewerMode { 107 FLYING, TILTING, WAITING_FOR_SEEK, WAITING_FOR_UP_PICK 108 }; 109 110 void constructor(SbBool build); 111 112 void dolly(const float delta) const; 113 void updateCursorRepresentation(void); // in SoNativeFlyViewer.cpp 114 void setMode(ViewerMode newmode); 115 int getMode(void) { return this->viewermode; } 116 117#define SO@GUI@_MIN_STEP 0.2f 118#define SO@GUI@_INC_FACTOR 1.2f 119#define SO@GUI@_MAX_SPEED 20.0f 120 121 SbTime * lastrender; 122 123 float currentspeed; 124 125 // Maximum speed, target for currentspeed during 126 // acceleration/decceleration. 127 float maxspeed; 128 129 // Scales speed. Calculated in updateSpeedScalingFactor. 130 float speed_scaling_factor; 131 132 // Used to calculate a new max_speed, on the basis on 'where' we are 133 // in the speed landscape, see {increment|decrement}MaxSpeed(). 134 int max_speed_factor; 135 136 // Speed 137 void incrementMaxSpeed(); 138 void decrementMaxSpeed(); 139 void updateMaxSpeed(); 140 void updateSpeedScalingFactor(); 141 void stopMoving(); 142 143 void updateSpeedIndicator(void); 144 void updateCameraPosition( SoCamera * camera, float speed, float dt ); 145 void updateCameraOrientation( SoCamera * camera, 146 float d_tilt, 147 float d_pan, 148 float dt ); 149 double calculateChangeInTime(); 150 void updateCurrentSpeed(double dt); 151 152 153 // Current keyboard state. 154 SbBool button1down; 155 SbBool button3down; 156 int lctrldown; 157 int rctrldown; 158 SbBool lshiftdown; 159 SbBool rshiftdown; 160 161 // View, speed display, renderingstate. 162 SoSearchAction * searcher; 163 164 SoNode * superimposition; 165 SoCoordinate3 * sgeometry; 166 167 SoScale * sscale; 168 SoScale * crossscale; 169 170 SoTranslation * stranslation; 171 SoTranslation * crossposition; 172 173 SoSwitch * smaxspeedswitch; 174 SoSwitch * scurrentspeedswitch; 175 SoSwitch * crossswitch; 176 177 SoNode * getSuperimpositionNode(const char * name); 178 179 void superimpositionevent(SoAction * action); 180 static void superimposition_cb(void * closure, SoAction * action); 181 182 183 // 184 float tilt_increment; // Angle-adjustment between View-up and direction 185 float pan_increment; // Rotation-adjustment around View-up 186 187 SbVec2s mouseloc; 188 SbVec2s lastpos; 189 SbVec2s tiltpos; 190 191 // FIXME: Refactor event handlers and PUBLIC(this)->processSoEvent 192 // in a way similar to that in SoGuiExaminerViewer, where only 193 // internal state is red/set. 20021017 rolvs 194 SbBool processKeyboardEvent(const SoKeyboardEvent * const kevt); 195 SbBool processMouseButtonEvent(const SoMouseButtonEvent * const mevt); 196 SbBool processLocation2Event(const SoLocation2Event * const levt); 197private: 198 So@Gui@FlyViewer * publ; 199 ViewerMode viewermode; 200}; 201 202So@Gui@FlyViewerP::So@Gui@FlyViewerP(So@Gui@FlyViewer * owner) 203{ 204 this->searcher = NULL; 205 this->publ = owner; 206 this->viewermode = FLYING; 207 this->currentspeed = 0.0f; 208 this->maxspeed = 0.0f; 209 this->speed_scaling_factor = 0.4f; 210 this->max_speed_factor = 0; 211 this->stranslation = NULL; 212 this->sscale = NULL; 213 this->button1down = FALSE; 214 this->button3down = FALSE; 215 this->lctrldown = 0; 216 this->rctrldown = 0; 217 this->lshiftdown = FALSE; 218 this->rshiftdown = FALSE; 219 this->lastrender = new SbTime; 220 this->tilt_increment = 0.0f; 221 this->pan_increment = 0.0f; 222} 223 224So@Gui@FlyViewerP::~So@Gui@FlyViewerP(void) 225{ 226 if ( this->searcher != NULL ) 227 delete this->searcher; 228 delete this->lastrender; 229 230 // superimposition unrefed in other destructor 231} 232 233#define PRIVATE(o) (o->pimpl) 234#define PUBLIC(o) (o->publ) 235 236 237// Common constructor code. 238void 239So@Gui@FlyViewerP::constructor(SbBool build) 240{ 241 PUBLIC(this)->setClassName(PUBLIC(this)->getDefaultWidgetName()); 242 243 static const char * superimposed[] = { 244 "#Inventor V2.1 ascii", 245 "", 246 "Separator {", 247 " MaterialBinding {", 248 " value OVERALL", 249 " }", 250 " OrthographicCamera {", 251 " height 1", 252 " nearDistance 0", 253 " farDistance 1", 254 " }", 255 " DEF so@gui@->callback Callback { }", 256 " Separator {", 257 " DEF so@gui@->translation Translation {", 258 " translation 0 0 0", 259 " }", 260 " DEF so@gui@->scale Scale {", 261 " scaleFactor 1 1 1", 262 " }", 263 " DEF so@gui@->geometry Coordinate3 {", 264 " point [", 265 " -0.8 -0.04 0,", 266 " -0.8 0 0,", 267 " -0.8 0.04 0,", 268 " 0 -0.04 0,", 269 " 0 0 0,", 270 " 0 0.04 0,", 271 " 0.8 -0.04 0,", 272 " 0.8 0 0,", 273 " 0.8 0.04 0,", 274 " 0 0.02 0,", // idx 9 275 " 0.8 0.02 0,", 276 " 0.8 -0.02 0,", 277 " 0 -0.02 0,", 278 " 0 0.01 0,", // idx 13 279 " 0.4 0.01 0,", 280 " 0.4 -0.01 0,", 281 " 0 -0.01 0", 282 " ]", 283 " }", 284 " DEF so@gui@->maxspeedswitch Switch {", 285 " whichChild -3", 286 // max speed indicator 287 " Material {", 288 " emissiveColor 1 0 0", 289 " }", 290 " IndexedFaceSet {", 291 " coordIndex [", 292 " 12, 11, 10, 9, -1", 293 " ]", 294 " }", 295 " }", 296 // the coordinate system 297 " BaseColor {", 298 " rgb 1 1 1", 299 " }", 300 " IndexedLineSet {", 301 " coordIndex [", 302 " 0, 2, -1,", 303 " 3, 5, -1,", 304 " 6, 8, -1,", 305 " 1, 7, -1", 306 " ]", 307 " }", 308 // current speed indicator 309 " DEF so@gui@->currentspeedswitch Switch {", 310 " whichChild -3", 311 " Material {", 312 " emissiveColor 0 0 1", 313 " }", 314 " IndexedFaceSet {", 315 " coordIndex [", 316 " 16, 15, 14, 13, -1", 317 " ]", 318 " }", 319 " }", 320 " }", 321 // cross 322 " DEF so@gui@->crossswitch Switch {", 323 " whichChild -1", 324 " DEF so@gui@->crossposition Translation {", 325 " translation 0 0 0", 326 " }", 327 " DEF so@gui@->crossscale Scale {", 328 " scaleFactor 1 1 1", 329 " }", 330 " BaseColor {", 331 " rgb 1 0 0", 332 " }", 333 " Coordinate3 {", 334 " point [", 335 " 0 -1 0,", 336 " 0 1 0,", 337 " -1 0 0,", 338 " 1 0 0", 339 " ]", 340 " }", 341 " IndexedLineSet {", 342 " coordIndex [", 343 " 0, 1, -1,", 344 " 2, 3, -1", 345 " ]", 346 " }", 347 " }", 348 "}", 349 NULL 350 }; 351 352 int i; 353 size_t bufsize; 354 for (i = 0, bufsize = 0; superimposed[i]; i++) 355 bufsize += strlen(superimposed[i]) + 1; 356 char * buf = new char [bufsize + 1]; 357 for (i = 0, bufsize = 0; superimposed[i]; i++) { 358 strcpy(buf + bufsize, superimposed[i]); 359 bufsize += strlen(superimposed[i]); 360 buf[bufsize] = '\n'; 361 bufsize++; 362 } 363 SoInput * input = new SoInput; 364 input->setBuffer(buf, bufsize); 365 SbBool ok = SoDB::read(input, this->superimposition); 366 assert(ok); 367 delete input; 368 delete [] buf; 369 this->superimposition->ref(); 370 371 372 this->sscale = (SoScale *) 373 this->getSuperimpositionNode("so@gui@->scale"); 374 this->stranslation = (SoTranslation *) 375 this->getSuperimpositionNode("so@gui@->translation"); 376 this->sgeometry = (SoCoordinate3 *) 377 this->getSuperimpositionNode("so@gui@->geometry"); 378 this->smaxspeedswitch = (SoSwitch *) 379 this->getSuperimpositionNode("so@gui@->maxspeedswitch"); 380 this->scurrentspeedswitch = (SoSwitch *) 381 this->getSuperimpositionNode("so@gui@->currentspeedswitch"); 382 this->crossswitch = (SoSwitch *) 383 this->getSuperimpositionNode("so@gui@->crossswitch"); 384 this->crossposition = (SoTranslation *) 385 this->getSuperimpositionNode("so@gui@->crossposition"); 386 this->crossscale = (SoScale *) 387 this->getSuperimpositionNode("so@gui@->crossscale"); 388 389 SoCallback * cb = (SoCallback *) 390 this->getSuperimpositionNode("so@gui@->callback"); 391 cb->setCallback(So@Gui@FlyViewerP::superimposition_cb, this); 392 393 this->updateSpeedIndicator(); 394 395 PUBLIC(this)->addSuperimposition(this->superimposition); 396 PUBLIC(this)->setSuperimpositionEnabled(this->superimposition,TRUE); 397 398 if (build) { 399 @WIDGET@ viewer = PUBLIC(this)->buildWidget(PUBLIC(this)->getParentWidget()); 400 PUBLIC(this)->setBaseWidget(viewer); 401 } 402} 403 404// This method dollies the camera back and forth in the scene. 405void 406So@Gui@FlyViewerP::dolly(const float delta) const 407{ 408 SoCamera * const camera = PUBLIC(this)->getCamera(); 409 if (camera == NULL) { return; } // if there's no scenegraph, for instance 410 411 SbPlane walkplane(PUBLIC(this)->getUpDirection(), 412 camera->position.getValue()); 413 414 SbVec3f campos = camera->position.getValue(); 415 SbVec3f camvec; 416 camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), camvec); 417 SbLine cross(campos + camvec, 418 campos + camvec + PUBLIC(this)->getUpDirection()); 419 SbVec3f intersect; 420 walkplane.intersect(cross, intersect); 421 SbVec3f dir = intersect - campos; 422 dir.normalize(); 423 424 camera->position = campos - dir * delta; 425} 426 427// The viewer is a state machine, and all changes to the current state 428// are made through this call. 429void 430So@Gui@FlyViewerP::setMode(ViewerMode newmode) 431{ 432 this->viewermode = newmode; 433 this->updateCursorRepresentation(); 434} 435 436// This method locates a named node in the superimposed scene. 437SoNode * 438So@Gui@FlyViewerP::getSuperimpositionNode(const char * name) 439{ 440 if (! this->searcher) 441 this->searcher = new SoSearchAction; 442 searcher->reset(); 443 searcher->setName(SbName(name)); 444 searcher->setInterest(SoSearchAction::FIRST); 445 searcher->setSearchingAll(TRUE); 446 searcher->apply(this->superimposition); 447 assert(searcher->getPath()); 448 return searcher->getPath()->getTail(); 449} 450 451SbBool 452So@Gui@FlyViewerP::processKeyboardEvent(const SoKeyboardEvent * const ke) 453{ 454 assert( ke != NULL ); 455 switch (ke->getState()) { 456 case SoButtonEvent::UP: 457 switch (ke->getKey()) { 458 case SoKeyboardEvent::U: 459 do { 460 // either to switch to up-vector pick mode, or back to fly 461 // mode if pick-mode already activated (ie cancel the 462 // up-vector pick operation) 463 SbBool uppickmode = 464 this->getMode() == So@Gui@FlyViewerP::WAITING_FOR_UP_PICK; 465 this->setMode(uppickmode ? So@Gui@FlyViewerP::FLYING : 466 So@Gui@FlyViewerP::WAITING_FOR_UP_PICK); 467 468 this->stopMoving(); 469 470 this->updateSpeedIndicator(); 471 PUBLIC(this)->scheduleRedraw(); 472 return TRUE; 473 } while (FALSE); 474 break; 475 476 case SoKeyboardEvent::S: 477 this->stopMoving(); 478 this->updateSpeedIndicator(); 479 PUBLIC(this)->scheduleRedraw(); 480 return FALSE; 481 482 case SoKeyboardEvent::LEFT_SHIFT: 483 this->lshiftdown = FALSE; 484 if (this->lshiftdown < 0) { 485#if SO@GUI@_DEBUG 486 SoDebugError::post("So@Gui@FlyViewerP::processKeyboardEvent", 487 "left shift key count < 0"); 488#endif 489 this->lshiftdown = 0; 490 } 491 break; 492 case SoKeyboardEvent::RIGHT_SHIFT: 493 this->rshiftdown = FALSE; 494 if (this->rshiftdown < 0) { 495#if SO@GUI@_DEBUG 496 SoDebugError::post("So@Gui@FlyViewerP::processKeyboardEvent", 497 "right shift key count < 0"); 498#endif 499 this->rshiftdown = 0; 500 } 501 break; 502 case SoKeyboardEvent::LEFT_CONTROL: 503 this->lctrldown -= 1; 504 if (this->lctrldown < 0) { 505#if SO@GUI@_DEBUG 506 SoDebugError::post("So@Gui@FlyViewerP::processKyeboardEvent", 507 "left control key count < 0"); 508#endif 509 this->lctrldown = 0; 510 } 511 break; 512 case SoKeyboardEvent::RIGHT_CONTROL: 513 this->rctrldown -= 1; 514 if (this->rctrldown < 0) { 515#if SO@GUI@_DEBUG 516 SoDebugError::post("So@Gui@FlyViewerP::processKyeboardEvent", 517 "right control key count < 0"); 518#endif 519 this->rctrldown = 0; 520 } 521 break; 522 default: 523 break; 524 } 525 break; 526 case SoButtonEvent::DOWN: 527 switch (ke->getKey()) { 528 case SoKeyboardEvent::LEFT_SHIFT: 529 this->lshiftdown += 1; 530 if (this->lshiftdown > 2) { 531#if SO@GUI@_DEBUG 532 SoDebugError::post("So@Gui@FlyViewerP::processKeyboardEvent", 533 "left shift key count > 2"); 534#endif 535 this->lshiftdown = 2; 536 } 537 break; 538 case SoKeyboardEvent::RIGHT_SHIFT: 539 this->rshiftdown += 1; 540 if (this->rshiftdown > 2) { 541#if SO@GUI@_DEBUG 542 SoDebugError::post("So@Gui@FlyViewerP::processKeyboardEvent", 543 "right shift key count > 2"); 544#endif 545 this->rshiftdown = 2; 546 } 547 break; 548 case SoKeyboardEvent::LEFT_CONTROL: 549 this->lctrldown += 1; 550 if (this->lctrldown > 2) { 551#if SO@GUI@_DEBUG 552 SoDebugError::post("So@Gui@FlyViewerP::processKeyboardEvent", 553 "left control key count > 2"); 554#endif 555 this->lctrldown = 2; 556 } 557 break; 558 case SoKeyboardEvent::RIGHT_CONTROL: 559 this->rctrldown += 1; 560 if (this->rctrldown > 2) { 561#if SO@GUI@_DEBUG 562 SoDebugError::post("So@Gui@FlyViewer::processSoEvent", 563 "right control key count > 2"); 564#endif 565 this->rctrldown = 2; 566 } 567 break; 568 default: 569 break; 570 } 571 break; 572 default: 573 break; 574 } 575 576 if ((this->getMode() == So@Gui@FlyViewerP::FLYING) && 577 (this->lctrldown || this->rctrldown)) { 578 this->setMode(So@Gui@FlyViewerP::TILTING); 579 580 this->tiltpos = this->mouseloc; 581 this->lastpos = this->mouseloc; 582 583 this->stopMoving(); 584 this->updateSpeedIndicator(); 585 this->crossswitch->whichChild.setValue(SO_SWITCH_ALL); 586 PUBLIC(this)->scheduleRedraw(); 587 // NOTE; this could be optimized to only draw the superimposition in 588 // question if speed is zero. 589 } else if ((this->getMode() == So@Gui@FlyViewerP::TILTING) && 590 !this->lctrldown && !this->rctrldown) { 591 this->setMode(So@Gui@FlyViewerP::FLYING); 592 assert(this->crossswitch != NULL); 593 this->crossswitch->whichChild.setValue(SO_SWITCH_NONE); 594 PUBLIC(this)->scheduleRedraw(); 595 } 596 return FALSE; 597} 598 599 600SbBool 601So@Gui@FlyViewerP::processMouseButtonEvent( const SoMouseButtonEvent * const me ) 602{ 603 assert( me != NULL ); 604 605 // FIXME: only for fly mode 606 switch (this->getMode()) { 607 case So@Gui@FlyViewerP::WAITING_FOR_UP_PICK: 608 if ((me->getButton() == SoMouseButtonEvent::BUTTON1) && 609 (me->getState() == SoButtonEvent::DOWN)) { 610 PUBLIC(this)->findUpDirection(me->getPosition()); 611 this->setMode(So@Gui@FlyViewerP::FLYING); 612 return TRUE; 613 } 614 break; 615 case So@Gui@FlyViewerP::FLYING: 616 switch (me->getButton()) { 617 case SoMouseButtonEvent::BUTTON1: 618 619 switch (me->getState()) { 620 621 case SoButtonEvent::DOWN: 622 // Incrementing speed. 623 this->button1down = TRUE; 624 if (this->button3down) { 625 this->stopMoving(); 626 } 627 else { 628 this->incrementMaxSpeed(); 629 } 630 this->updateSpeedIndicator(); 631 PUBLIC(this)->scheduleRedraw(); 632 return TRUE; 633 case SoButtonEvent::UP: 634 this->button1down = FALSE; 635 return TRUE; 636 default: 637 break; 638 } 639 break; 640 641 case SoMouseButtonEvent::BUTTON3: 642 643 switch (me->getState()) { 644 case SoButtonEvent::DOWN: 645 this->button3down = TRUE; 646 647 if (this->button1down) { 648 this->stopMoving(); 649 } 650 else 651 this->decrementMaxSpeed(); 652 653 this->updateSpeedIndicator(); 654 PUBLIC(this)->scheduleRedraw(); 655 return TRUE; 656 case SoButtonEvent::UP: 657 this->button3down = FALSE; 658 return TRUE; 659 default: 660 break; 661 } 662 break; 663 default: 664 break; 665 } 666 default: 667 break; 668 } 669 return FALSE; 670} 671 672SbBool 673So@Gui@FlyViewerP::processLocation2Event(const SoLocation2Event * const lev) 674{ 675 this->mouseloc = lev->getPosition(); 676 677 if (this->getMode() == So@Gui@FlyViewerP::TILTING) { 678 679 float pan = (this->lastpos[0] - this->mouseloc[0])/100.0f; 680 float tilt = (this->lastpos[1] - this->mouseloc[1])/100.0f; 681 682 SoCamera * camera = PUBLIC(this)->getCamera(); 683 if (camera == NULL) 684 return TRUE; // probably sceneless 685 686 this->updateCameraOrientation( camera, tilt, pan, 1.0f ); 687 this->lastpos = this->mouseloc; 688 } 689 690 // FIXME: The size of the glcanvas only changes when the viewer is 691 // resized. The GLSize should be set from the FlyViewer, to remove 692 // the dependency on the PUBLIC(this) class. 20021021 rolvs 693 SbVec2s glsize( PUBLIC(this)->getGLSize() ); 694 695 // NOTE: The values are normalized, so that the FlyViewer behaves 696 // the same way no matter the screen-size. The old way to do it made 697 // the possible range of pan and tilt increment depend on the canvas 698 // size. 20021022 rolvs. 699 this->pan_increment = 0.5f - float(this->mouseloc[0])/glsize[0]; 700 this->tilt_increment = 0.5f - float(this->mouseloc[1])/glsize[1]; 701 702 return TRUE; 703} 704 705void 706So@Gui@FlyViewerP::superimpositionevent(SoAction * action) 707{ 708 if (!action->isOfType(SoGLRenderAction::getClassTypeId())) return; 709 SbViewportRegion vpRegion = 710 ((SoGLRenderAction *) action)->getViewportRegion(); 711 SbVec2s viewport = vpRegion.getViewportSizePixels(); 712 float aspect = float(viewport[0]) / float(viewport[1]); 713 float factorx = 1.0f/float(viewport[1]) * 220.0f; 714 float factory = factorx; 715 if (aspect > 1.0f) { 716 this->stranslation->translation.setValue(SbVec3f(0.0f, -0.4f, 0.0f)); 717 } else { 718 this->stranslation->translation.setValue(SbVec3f(0.0f, -0.4f / aspect, 0.0f)); 719 factorx /= aspect; 720 factory /= aspect; 721 } 722 if (viewport[0] > 500) 723 factorx *= 500.0f / 400.0f; 724 else 725 factorx *= float(viewport[0]) / 400.0f; 726 this->sscale->scaleFactor.setValue(SbVec3f(factorx, factory, 1.0f)); 727 728 if (this->getMode() == TILTING) { 729 assert(this->crossposition != NULL); 730 assert(this->crossscale != NULL); 731 float tx = float(this->tiltpos[0]-float(viewport[0])/2.0f)/(float(viewport[0])); 732 float ty = float(this->tiltpos[1]-float(viewport[1])/2.0f)/(float(viewport[1])); 733 if (aspect > 1.0f) tx *= aspect; 734 else ty /= aspect; 735 this->crossposition->translation.setValue(SbVec3f(tx, ty, 0)); 736 737 float sx = (1.0f/float(viewport[0])) * 15.0f; 738 float sy = (1.0f/float(viewport[1])) * 15.0f; 739 if (aspect > 1.0f) sx *= aspect; 740 else sy /= aspect; 741 this->crossscale->scaleFactor.setValue(SbVec3f(sx, sy, 0)); 742 } 743} 744 745void 746So@Gui@FlyViewerP::superimposition_cb(void * closure, SoAction * action) 747{ 748 assert(closure != NULL); 749 ((So@Gui@FlyViewerP *) closure)->superimpositionevent(action); 750} 751 752void 753So@Gui@FlyViewerP::updateSpeedIndicator(void) 754{ 755 assert(this->sgeometry != NULL); 756 757 SbVec3f * points = this->sgeometry->point.startEditing(); 758 759 if (points[10][0] == 0.0f) 760 this->smaxspeedswitch->whichChild.setValue(SO_SWITCH_ALL); 761 if (points[14][0] == 0.0f) 762 this->scurrentspeedswitch->whichChild.setValue(SO_SWITCH_ALL); 763 points[10][0] = this->maxspeed / (SO@GUI@_MAX_SPEED / 0.8f); 764 points[11][0] = this->maxspeed / (SO@GUI@_MAX_SPEED / 0.8f); 765 points[14][0] = this->currentspeed / (SO@GUI@_MAX_SPEED / 0.8f); 766 points[15][0] = this->currentspeed / (SO@GUI@_MAX_SPEED / 0.8f); 767 this->sgeometry->point.finishEditing(); 768 769 if (this->maxspeed == 0.0f) 770 this->smaxspeedswitch->whichChild.setValue(SO_SWITCH_NONE); 771 if (this->currentspeed == 0.0f) 772 this->scurrentspeedswitch->whichChild.setValue(SO_SWITCH_NONE); 773} 774 775double So@Gui@FlyViewerP::calculateChangeInTime() 776{ 777 SbTime thisrender; 778 thisrender.setToTimeOfDay(); 779 780 if (this->currentspeed == 0.0f) 781 this->lastrender->setValue(thisrender.getValue() - 0.01); 782 783 // We've had a report on Coin-support that floats may have too low 784 // precision for the subtraction of these two values (ie it becomes 785 // zero), so don't cast to float. 786 // 787 // FIXME: it doesn't sound likely that this was the real cause of 788 // the problem. First of all, it seems improbably that precision 789 // could be so bad for floats, as the time between render frames 790 // should almost be guaranteed to be milliseconds, at least. It is 791 // suspicious that the error only shows up with the Intel C++ 792 // compiler, and not when the reported built with MSVC++ instead. 793 // 794 // Second, the fix is not sufficient. What if the 795 // SbTime::getTimeOfDay() resolution is too low on the particular 796 // system, so we often get zero difference here? That case must be 797 // handled, and from the original bug report, it sounds like it 798 // isn't, which is a separate bug in itself. 799 // 800 // Third, what's up with that magic multiplication factor of 10? 801 // That doesn't seem to make sense. 802 // 803 // 20061212 mortene. 804 805 // This is only a problem on release builds which makes it sound 806 // like it's just some value that is not properly 807 // initialized. (20061212 frodo) 808 809 double t = (thisrender.getValue() - this->lastrender->getValue()) * 10.0; 810 811 if (t >= 1.0) 812 t = 1.0; 813 814 return t; 815} 816 817void So@Gui@FlyViewerP::updateCurrentSpeed(double dt) 818{ 819 float speedscale = 820 1.0f - (this->pan_increment * this->pan_increment 821 + this->tilt_increment * this->tilt_increment); 822 823 // NOTE: I don't believe that this boundary condition could ever 824 // happen. 20021022 rolvs 825 if (speedscale < 0.0f) 826 speedscale = 0.0f; 827 828 this->currentspeed += 829 (((this->currentspeed + 830 this->maxspeed * speedscale) / 2.0f) - 831 this->currentspeed) * float(dt); 832} 833 834void So@Gui@FlyViewerP::updateCameraPosition(SoCamera * camera, 835 float current_speed, 836 float dt) 837{ 838 assert(camera != NULL); 839 SbVec3f dir; 840 camera->orientation.getValue().multVec(SbVec3f(0, 0, -1), dir); 841 dir.normalize(); 842 camera->position.setValue(camera->position.getValue() + 843 dir * (current_speed * dt)); 844} 845 846void So@Gui@FlyViewerP::updateCameraOrientation(SoCamera * camera, 847 float d_tilt, 848 float d_pan, 849 float dt) 850{ 851 assert(camera != NULL); 852 // FIXME: Make sure that the angle between direction and up-vector 853 // stays larger than zero, or else it gets 'locked' in an undefined 854 // state and starts to act weird. This should probably be done in 855 // parent class. 20021017 rolvs 856 PUBLIC(this)->tiltCamera(d_tilt * dt); 857 858 camera->orientation = camera->orientation.getValue() * 859 SbRotation(PUBLIC(this)->getUpDirection(), d_pan * dt); 860} 861 862void 863So@Gui@FlyViewerP::incrementMaxSpeed(void) 864{ 865 this->max_speed_factor++; 866 this->updateMaxSpeed(); 867} 868 869 870void 871So@Gui@FlyViewerP::decrementMaxSpeed(void) 872{ 873 this->max_speed_factor--; 874 this->updateMaxSpeed(); 875} 876 877 878void 879So@Gui@FlyViewerP::updateSpeedScalingFactor(void) 880{ 881 SoNode * n = PUBLIC(this)->getSceneGraph(); 882 if(n == NULL) 883 return; // Scenegraph not set yet? 884 885 SoGetBoundingBoxAction bbact(PUBLIC(this)->getViewportRegion()); 886 bbact.apply(n); 887 888 SbBox3f bbox = bbact.getBoundingBox(); 889 float bbox_diagonal = (bbox.getMax() - bbox.getMin()).length(); 890 891 // FIXME: It should be possible to create a simple scaling function, 892 // based on some logaritmic evaluation. 20021017 rolvs. 893 if (bbox_diagonal > 100) 894 this->speed_scaling_factor = 1.0f; // log(bbox_diagonal); 895 else if (bbox_diagonal > 10 && bbox_diagonal < 100) 896 this->speed_scaling_factor = 0.4f; 897 else if (bbox_diagonal > 1 && bbox_diagonal < 10) 898 this->speed_scaling_factor = 0.3f; 899 else if (bbox_diagonal > 0.1 && bbox_diagonal < 1) 900 this->speed_scaling_factor = 0.1f; 901 else 902 this->speed_scaling_factor = 0.1f * bbox_diagonal; 903} 904 905void So@Gui@FlyViewerP::stopMoving(void) 906{ 907 this->maxspeed = 0.0f; 908 this->currentspeed = 0.0f; 909 this->max_speed_factor = 0; 910} 911 912void So@Gui@FlyViewerP::updateMaxSpeed(void) 913{ 914 if (this->max_speed_factor == 0) { 915 this->stopMoving(); 916 return; 917 } 918 919 // FIXME: Move this methodcall so that it is called only 920 // once. (e.g. when scene graph is set) 20021021 rolvs 921 this->updateSpeedScalingFactor(); 922 923 this->maxspeed = 924 this->max_speed_factor 925 * float(pow(SO@GUI@_INC_FACTOR, abs( this->max_speed_factor))) 926 * this->speed_scaling_factor; 927 928 if (this->maxspeed > SO@GUI@_MAX_SPEED) 929 this->maxspeed = SO@GUI@_MAX_SPEED; 930 else if (this->maxspeed < -1*SO@GUI@_MAX_SPEED) 931 this->maxspeed = -1 * SO@GUI@_MAX_SPEED; 932} 933 934 935 936// Set cursor graphics according to mode. 937void 938So@Gui@FlyViewerP::updateCursorRepresentation(void) 939{ 940 if (!PUBLIC(this)->isCursorEnabled()) { 941 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getBlankCursor()); 942 return; 943 } 944 945 switch (this->viewermode) { 946 case So@Gui@FlyViewerP::FLYING: 947 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::DEFAULT)); 948 break; 949 950 case So@Gui@FlyViewerP::WAITING_FOR_SEEK: 951 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::CROSSHAIR)); 952 break; 953 954 case So@Gui@FlyViewerP::WAITING_FOR_UP_PICK: 955 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::UPARROW)); 956 break; 957 958 case So@Gui@FlyViewerP::TILTING: 959 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getPanCursor()); 960 break; 961 962 default: 963 assert(0 && "unknown mode"); 964 break; 965 } 966} 967 968#endif // DOXYGEN_SKIP_THIS 969 970// ************************************************************************ 971 972SO@GUI@_OBJECT_SOURCE(So@Gui@FlyViewer); 973 974// ************************************************************************ 975 976/*! 977 Public constructor. 978*/ 979So@Gui@FlyViewer::So@Gui@FlyViewer(@WIDGET@ parent, 980 const char * name, 981 SbBool embed, 982 So@Gui@FullViewer::BuildFlag flag, 983 So@Gui@Viewer::Type type) 984 : inherited(parent, name, embed, flag, type, FALSE) 985{ 986 PRIVATE(this) = new So@Gui@FlyViewerP(this); 987 PRIVATE(this)->constructor(TRUE); 988} 989 990// ************************************************************************ 991 992/*! 993 Protected constructor, used by viewer components derived from the 994 So@Gui@FlyViewer. 995*/ 996So@Gui@FlyViewer::So@Gui@FlyViewer(@WIDGET@ parent, 997 const char * const name, 998 SbBool embed, 999 So@Gui@FullViewer::BuildFlag flag, 1000 So@Gui@Viewer::Type type, 1001 SbBool build) 1002 : inherited(parent, name, embed, flag, type, FALSE) 1003{ 1004 PRIVATE(this) = new So@Gui@FlyViewerP(this); 1005 PRIVATE(this)->constructor(build); 1006} 1007 1008// ************************************************************************ 1009 1010/*! 1011 Virtual constructor. 1012*/ 1013So@Gui@FlyViewer::~So@Gui@FlyViewer() 1014{ 1015 if (PRIVATE(this)->superimposition != NULL) { 1016 this->removeSuperimposition(PRIVATE(this)->superimposition); 1017 PRIVATE(this)->superimposition->unref(); 1018 PRIVATE(this)->superimposition = NULL; 1019 } 1020 delete PRIVATE(this); 1021} 1022 1023// ************************************************************************ 1024 1025// doc in super 1026void 1027So@Gui@FlyViewer::setViewing(SbBool enable) 1028{ 1029 if (enable != this->isViewing()) 1030 PRIVATE(this)->stopMoving(); 1031 1032 inherited::setViewing(enable); 1033 this->setSuperimpositionEnabled(PRIVATE(this)->superimposition, enable); 1034 this->scheduleRedraw(); 1035} 1036 1037// ************************************************************************ 1038 1039// doc in super 1040void 1041So@Gui@FlyViewer::resetToHomePosition(void) 1042{ 1043 PRIVATE(this)->stopMoving(); 1044 inherited::resetToHomePosition(); 1045} 1046 1047// ************************************************************************ 1048 1049// doc in super 1050void 1051So@Gui@FlyViewer::viewAll(void) 1052{ 1053 PRIVATE(this)->stopMoving(); 1054 inherited::viewAll(); 1055} 1056 1057// ************************************************************************ 1058 1059// doc in super 1060void 1061So@Gui@FlyViewer::setCamera(SoCamera * camera) 1062{ 1063 PRIVATE(this)->stopMoving(); 1064 1065 inherited::setCamera(camera); 1066 // FIXME: do something with up-direction? 1067} 1068 1069// ************************************************************************ 1070 1071// doc in super 1072void 1073So@Gui@FlyViewer::setCursorEnabled(SbBool enable) 1074{ 1075 inherited::setCursorEnabled(enable); 1076 PRIVATE(this)->updateCursorRepresentation(); 1077} 1078 1079// ************************************************************************ 1080 1081// doc in super 1082void 1083So@Gui@FlyViewer::setCameraType(SoType type) 1084{ 1085 PRIVATE(this)->stopMoving(); 1086 inherited::setCameraType(type); 1087 // FIXME: what else? 20010907 mortene. 1088} 1089 1090// ************************************************************************ 1091 1092// doc in super 1093const char * 1094So@Gui@FlyViewer::getDefaultWidgetName(void) const 1095{ 1096 static const char defaultWidgetName[] = "So@Gui@FlyViewer"; 1097 return defaultWidgetName; 1098} 1099 1100// ************************************************************************ 1101 1102// doc in super 1103const char * 1104So@Gui@FlyViewer::getDefaultTitle(void) const 1105{ 1106 static const char defaultTitle[] = "Fly Viewer"; 1107 return defaultTitle; 1108} 1109 1110// ************************************************************************ 1111 1112// doc in super 1113const char * 1114So@Gui@FlyViewer::getDefaultIconTitle(void) const 1115{ 1116 static const char defaultIconTitle[] = "Fly Viewer"; 1117 return defaultIconTitle; 1118} 1119 1120// ************************************************************************ 1121 1122// Documented in superclass. 1123SbBool 1124So@Gui@FlyViewer::processSoEvent(const SoEvent * const event) 1125{ 1126 // FIXME: Refactor the event-handling so that it uses the same 1127 // strategy as in So@Gui@ExaminerViewer, where the event-handler 1128 // only checks the state and the mode from that. 20021016 rolvs. 1129 1130 // We're in "interact" mode (ie *not* the camera modification mode), 1131 // so don't handle the event here. It should either be forwarded to 1132 // the scenegraph, or caught by So@Gui@Viewer::processSoEvent() if 1133 // it's an ESC and ALT press (to switch modes). 1134 if (!this->isViewing()) { return inherited::processSoEvent(event); } 1135 1136 // Events when in "ready-to-seek" mode are ignored, except those 1137 // which influence the seek mode itself -- these are handled further 1138 // up the inheritance hierarchy. 1139 if (this->isSeekMode()) { return inherited::processSoEvent(event); } 1140 1141 // FIXME: There is more parts of the code in 1142 // So@Gui@*FlyViewer::processEvent that should go in to the 1143 // processKeyboardEvent function; to be fixed later. 1144 // 20021015 rolvs 1145 1146 // Keyboard handling 1147 if (event->isOfType(SoKeyboardEvent::getClassTypeId())) { 1148 SbBool result = 1149 PRIVATE(this)->processKeyboardEvent( (SoKeyboardEvent*)event ); 1150 1151 if( result ){ 1152 return TRUE; 1153 } 1154 // Else: Do nothing, and proceed as usual 1155 } 1156 1157 // Mousebutton handling 1158 // See FIXME and comment for keyboardhandler. 1159 else if (event->isOfType(SoMouseButtonEvent::getClassTypeId())) { 1160 // FIXME: only for fly mode 1161 const SoMouseButtonEvent * const me = 1162 (const SoMouseButtonEvent *const) event; 1163 SbBool result = PRIVATE( this )->processMouseButtonEvent( me ); 1164 if( result ) 1165 return TRUE; 1166 } 1167 1168 else if (event->isOfType(SoLocation2Event::getClassTypeId())) { 1169 const SoLocation2Event * const le = 1170 (const SoLocation2Event * const) event; 1171 SbBool result = PRIVATE( this )->processLocation2Event( le ); 1172 if( result ) 1173 return TRUE; 1174 } 1175 1176 return inherited::processSoEvent(event); 1177} 1178 1179// ************************************************************************ 1180 1181// doc in super 1182void 1183So@Gui@FlyViewer::setSeekMode(SbBool enable) 1184{ 1185 // Note: this method is almost identical to the setSeekMode() in the 1186 // So@Gui@ExaminerViewer, so migrate any changes. 1187 1188#if SO@GUI@_DEBUG 1189 if (enable == this->isSeekMode()) { 1190 SoDebugError::postWarning("So@Gui@FlyViewer::setSeekMode", 1191 "seek mode already %sset", enable ? "" : "un"); 1192 return; 1193 } 1194#endif // SO@GUI@_DEBUG 1195 1196 // FIXME: what if we're in the middle of a seek already? 20010910 mortene. 1197 // larsa - either stop the seek (on false) or reset timer to two new secs 1198 1199 inherited::setSeekMode(enable); 1200 PRIVATE(this)->setMode(enable ? So@Gui@FlyViewerP::WAITING_FOR_SEEK : 1201 So@Gui@FlyViewerP::FLYING); 1202} 1203 1204// ************************************************************************ 1205 1206// doc in super 1207void 1208So@Gui@FlyViewer::actualRedraw(void) 1209{ 1210 if (!this->isViewing()) { 1211 inherited::actualRedraw(); 1212 return; 1213 } 1214 1215 switch (PRIVATE(this)->getMode()) { 1216 case So@Gui@FlyViewerP::FLYING: 1217 { 1218 PRIVATE(this)->updateCurrentSpeed(PRIVATE(this)->calculateChangeInTime()); 1219 PRIVATE(this)->updateSpeedIndicator(); 1220 1221 SbTime thisrender; 1222 thisrender.setToTimeOfDay(); 1223 1224 if (PRIVATE(this)->currentspeed != 0.0f) { 1225 // We've had a report on Coin-support that floats may have too 1226 // low precision for the subtraction of these two values (ie 1227 // it becomes zero), so don't cast to float. 1228 // 1229 // Note: there's some additional information about this, see 1230 // the FIXME comment in the 1231 // So@Gui@FlyViewerP::calculateChangeInTime() function. 1232 double t = (thisrender.getValue() - 1233 PRIVATE(this)->lastrender->getValue()) * 2.0; 1234 if (t > 0.0) { 1235 SoCamera * camera = this->getCamera(); 1236 1237 if (camera){ // could be a sceneless viewer 1238 PRIVATE(this)->updateCameraPosition 1239 ( camera, 1240 PRIVATE(this)->currentspeed* 1241 PRIVATE(this)->speed_scaling_factor, 1242 float(t) ); 1243 PRIVATE(this)->updateCameraOrientation 1244 ( camera, 1245 PRIVATE(this)->tilt_increment, 1246 PRIVATE(this)->pan_increment, 1247 float(t) ); 1248 } 1249 } 1250 } 1251 1252 inherited::actualRedraw(); 1253 1254 PRIVATE(this)->lastrender->setValue(thisrender.getValue()); 1255 1256 if (PRIVATE(this)->currentspeed != 0.0f || 1257 PRIVATE(this)->maxspeed != 0.0f) 1258 this->scheduleRedraw(); 1259 } 1260 break; 1261 default: 1262 inherited::actualRedraw(); 1263 break; 1264 } 1265} 1266 1267// ************************************************************************ 1268 1269// doc in super 1270void 1271So@Gui@FlyViewer::rightWheelMotion(float value) 1272{ 1273 PRIVATE(this)->dolly(value - this->getRightWheelValue()); 1274 inherited::rightWheelMotion(value); 1275} 1276 1277// ************************************************************************ 1278 1279// doc in super 1280void 1281So@Gui@FlyViewer::afterRealizeHook(void) 1282{ 1283 PRIVATE(this)->updateCursorRepresentation(); 1284 inherited::afterRealizeHook(); 1285} 1286 1287// ************************************************************************ 1288 1289#undef PRIVATE 1290#undef PUBLIC 1291 1292