00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055 #include "QtBaseViewer.hh"
00056 #include "QtGLViewerLayout.hh"
00057 #include "CursorPainter.hh"
00058 #include <ACG/QtWidgets/QtWheel.hh>
00059 #include <ACG/Scenegraph/DrawModes.hh>
00060 #include <ACG/GL/gl.hh>
00061
00062 #include <iostream>
00063 #include <string>
00064 #include <assert.h>
00065
00066 #include <QMimeData>
00067 #include <QToolButton>
00068 #include <QFrame>
00069
00070 #include <QClipboard>
00071 #include <QApplication>
00072 #include <QSplitter>
00073 #include <QLayout>
00074 #include <QPushButton>
00075 #include <QLabel>
00076 #include <QImage>
00077 #include <QColorDialog>
00078 #include <QFileDialog>
00079 #include <QToolTip>
00080 #include <QTextStream>
00081 #include <QDateTime>
00082 #include <QTimer>
00083
00084 #include <QDesktopWidget>
00085 #include <QMouseEvent>
00086 #include <QVBoxLayout>
00087 #include <QKeyEvent>
00088 #include <QGridLayout>
00089 #include <QContextMenuEvent>
00090 #include <QWheelEvent>
00091 #include <QDropEvent>
00092 #include <QPixmap>
00093 #include <QMenu>
00094 #include <QVariant>
00095 #include <QButtonGroup>
00096 #include <QToolBar>
00097
00098 #include <QGraphicsView>
00099 #include <QGraphicsScene>
00100 #include <QGraphicsWidget>
00101 #include <QGraphicsGridLayout>
00102 #include <QGraphicsProxyWidget>
00103 #include <QPainter>
00104 #include <QPaintEngine>
00105
00106 #ifdef max
00107 # undef max
00108 #endif
00109
00110 #ifdef min
00111 # undef min
00112 #endif
00113
00114 #include <OpenFlipper/BasePlugin/PluginFunctions.hh>
00115 #include <OpenFlipper/common/ViewObjectMarker.hh>
00116
00117 #include <OpenFlipper/common/GlobalOptions.hh>
00118
00119 #include <QGLFramebufferObject>
00120
00121
00122
00123
00124
00125
00126
00127 static const char VIEW_MAGIC[] =
00128 "ACG::QtWidgets::QGLViewerWidget encoded view";
00129
00130
00131
00132
00133 glViewer::glViewer( QGraphicsScene* _scene,
00134 QGLWidget* _glWidget,
00135 Viewer::ViewerProperties& _properties,
00136 QGraphicsWidget* _parent) :
00137 QGraphicsWidget(_parent),
00138 glareaGrabbed_(false),
00139 projectionUpdateLocked_(false),
00140 blending_(true),
00141 glScene_(_scene),
00142 glWidget_(_glWidget),
00143 cursorPainter_(0),
00144 cursorPositionValid_(false),
00145 pickCache_(0),
00146 updatePickCache_(true),
00147 pickCacheSupported_(true),
00148 clickEvent_(QEvent::MouseButtonPress, QPoint (), Qt::NoButton, Qt::NoButton, Qt::NoModifier),
00149 properties_(_properties),
00150 glstate_(0),
00151 initialized_(false)
00152 {
00153
00154
00155 createWidgets();
00156
00157
00158 glstate_ = new ACG::GLState();
00159 properties_.setglState( glstate_ );
00160
00161
00162 orthoWidth_ = 2.0;
00163 isRotating_ = false;
00164 lookAround_ = false;
00165 near_ = 0.1;
00166 far_ = 100.0;
00167 fovy_ = 45.0;
00168
00169 sceneGraphRoot_ = 0;
00170
00171 normalsMode_ = NORMALIZE_NORMALS;
00172 projectionMode_ = PERSPECTIVE_PROJECTION;
00173 navigationMode_ = NORMAL_NAVIGATION;
00174
00175
00176 light_matrix_.identity();
00177
00178 trackMouse_ = false;
00179
00180
00181 stereo_ = false;
00182
00183
00184
00185
00186 QSizePolicy sp = sizePolicy();
00187 sp.setHorizontalPolicy( QSizePolicy::Expanding );
00188 sp.setVerticalPolicy( QSizePolicy::Expanding );
00189 sp.setHorizontalStretch( 1 );
00190 sp.setVerticalStretch( 1 );
00191 setSizePolicy( sp );
00192
00193 redrawTime_.start ();
00194
00195
00196 timer_ = new QTimer( this );
00197 connect( timer_, SIGNAL(timeout()), this, SLOT( slotAnimation()) );
00198
00199 allowRotation_ = true;
00200
00201
00202 connect( &properties_,SIGNAL(updated()), this, SLOT( slotPropertiesUpdated() ) );
00203
00204
00205
00206 slotPropertiesUpdated();
00207
00208 setAcceptDrops(true);
00209
00210 setHome();
00211
00212
00213 agTexWidth_ = 0;
00214 agTexHeight_ = 0;
00215 agTexture_[0] = 0;
00216 agTexture_[1] = 0;
00217 agProgram_ = 0;
00218 customAnaglyphSupported_ = false;
00219
00220 clickTimer_.setSingleShot (true);
00221 connect (&clickTimer_, SIGNAL(timeout ()), this, SLOT(slotClickTimeout ()));
00222 }
00223
00224
00225
00226
00227
00228 glViewer::~glViewer()
00229 {
00230 finiCustomAnaglyphStereo ();
00231 delete glstate_;
00232 }
00233
00234
00235
00236
00237
00238 QSize
00239 glViewer::sizeHint() const
00240 {
00241 return QSize( 600, 600 );
00242 }
00243
00244
00245
00246
00247 void glViewer::makeCurrent() {
00248 glWidget_->makeCurrent();
00249 }
00250
00251 void glViewer::swapBuffers() {
00252 glWidget_->swapBuffers();
00253 }
00254
00255
00256
00257
00258
00259 void glViewer::sceneGraph(ACG::SceneGraph::BaseNode* _root, const bool _resetTrackBall)
00260 {
00261 sceneGraphRoot_ = _root;
00262
00263 if (sceneGraphRoot_ )
00264 {
00265
00266 ACG::SceneGraph::BoundingBoxAction act;
00267 ACG::SceneGraph::traverse(sceneGraphRoot_, act);
00268
00269 ACG::Vec3d bbmin = (ACG::Vec3d) act.bbMin();
00270 ACG::Vec3d bbmax = (ACG::Vec3d) act.bbMax();
00271
00272 if ( ( bbmin[0] > bbmax[0] ) ||
00273 ( bbmin[1] > bbmax[1] ) ||
00274 ( bbmin[2] > bbmax[2] ) )
00275 setScenePos( ACG::Vec3d( 0.0,0.0,0.0 ) , 1.0, _resetTrackBall );
00276 else
00277 setScenePos( ( bbmin + bbmax ) * 0.5,
00278 ( bbmax - bbmin ).norm() * 0.5,
00279 _resetTrackBall);
00280 }
00281
00282 updateGL();
00283
00284 emit(signalSceneGraphChanged(sceneGraphRoot_));
00285 }
00286
00287
00288
00289
00290
00291 void glViewer::trackMouse(bool _track)
00292 {
00293 trackMouse_ = _track;
00294 }
00295
00296
00297
00298
00299 void glViewer::perspectiveProjection()
00300 {
00301 projectionMode(PERSPECTIVE_PROJECTION);
00302 updateGL();
00303 }
00304
00305
00306 void glViewer::orthographicProjection()
00307 {
00308 projectionMode(ORTHOGRAPHIC_PROJECTION);
00309 updateGL();
00310 }
00311
00312
00313 void glViewer::toggleProjectionMode()
00314 {
00315 if (projectionMode_ == ORTHOGRAPHIC_PROJECTION)
00316 projectionMode(PERSPECTIVE_PROJECTION);
00317 else
00318 projectionMode(ORTHOGRAPHIC_PROJECTION);
00319
00320 updateGL();
00321 }
00322
00323
00324 void glViewer::projectionMode(ProjectionMode _p)
00325 {
00326 if ((projectionMode_ = _p) == ORTHOGRAPHIC_PROJECTION)
00327 emit projectionModeChanged( true );
00328 else
00329 emit projectionModeChanged( false );
00330
00331 updateProjectionMatrix();
00332
00333 emit viewChanged();
00334 }
00335
00336 void glViewer::toggleNavigationMode()
00337 {
00338 if (navigationMode_ == NORMAL_NAVIGATION)
00339 navigationMode(FIRSTPERSON_NAVIGATION);
00340 else
00341 navigationMode(NORMAL_NAVIGATION);
00342 }
00343
00344
00345 void glViewer::navigationMode(NavigationMode _n)
00346 {
00347 if ((navigationMode_ = _n) == NORMAL_NAVIGATION)
00348 emit navigationModeChanged( true );
00349 else
00350 emit navigationModeChanged( false );
00351 }
00352
00353
00354 void glViewer::updateProjectionMatrix()
00355 {
00356 if( projectionUpdateLocked_ )
00357 return;
00358
00359 makeCurrent();
00360
00361 glstate_->reset_projection();
00362
00363
00364 if (stereo_ || projectionMode_ == PERSPECTIVE_PROJECTION)
00365 {
00366 double aspect;
00367
00368 if (isVisible() && glWidth() && glHeight())
00369 aspect = (double) glWidth() / (double) glHeight();
00370 else
00371 aspect = 1.0;
00372
00373 glstate_->perspective(fovy_, (GLdouble) aspect,
00374 near_, far_);
00375 }
00376 else
00377 {
00378 double aspect;
00379
00380 if (isVisible() && glWidth() && glHeight())
00381 aspect = (double) glWidth() / (double) glHeight();
00382 else
00383 aspect = 1.0;
00384
00385 glstate_->ortho( -orthoWidth_, orthoWidth_,
00386 -orthoWidth_/aspect, orthoWidth_/aspect,
00387 near_, far_ );
00388 }
00389
00390 }
00391
00392
00393
00394
00395
00396 void glViewer::setScenePos(const ACG::Vec3d& _center, double _radius, const bool _resetTrackBall)
00397 {
00398 if(_resetTrackBall) {
00399 trackball_center_ = _center;
00400 }
00401
00402 scene_center_ = _center;
00403
00404 scene_radius_ = trackball_radius_ = _radius;
00405
00406 ACG::Vec3d c = glstate_->modelview().transform_point(scene_center_);
00407
00408
00409 far_ = std::max(0.0002f * scene_radius_, -(c[2] - scene_radius_));
00410
00411
00412 near_ = std::max(0.0001f * scene_radius_, -(c[2] + scene_radius_));
00413
00414
00415 updateProjectionMatrix();
00416 updateGL();
00417
00418 emit viewChanged();
00419 }
00420
00421
00422
00423 void glViewer::setSceneCenter( const ACG::Vec3d& _center ) {
00424
00425 scene_center_ = _center;
00426 }
00427
00428
00429
00430 void glViewer::setTrackBallCenter( const ACG::Vec3d& _center ) {
00431
00432 trackball_center_ = _center;
00433 }
00434
00435
00436
00437
00438 void glViewer::viewingDirection( const ACG::Vec3d& _dir, const ACG::Vec3d& _up )
00439 {
00440
00441 ACG::Vec3d eye = scene_center_ - _dir*(3.0*scene_radius_);
00442
00443 glstate_->reset_modelview();
00444 glstate_->lookAt((ACG::Vec3d)eye, (ACG::Vec3d)scene_center_, (ACG::Vec3d)_up);
00445
00446 emit viewChanged();
00447 }
00448
00449
00450
00451
00452
00453 void glViewer::normalsMode(NormalsMode _mode)
00454 {
00455 makeCurrent();
00456
00457 switch(normalsMode_ = _mode)
00458 {
00459 case DONT_TOUCH_NORMALS:
00460 glDisable(GL_NORMALIZE);
00461 break;
00462
00463 case NORMALIZE_NORMALS:
00464 glEnable(GL_NORMALIZE);
00465 break;
00466 }
00467
00468 updateGL();
00469 }
00470
00471
00472
00473
00474
00475 void
00476 glViewer::copyToImage( QImage& _image,
00477 unsigned int _l, unsigned int _t,
00478 unsigned int _w, unsigned int _h,
00479 GLenum )
00480 {
00481
00482
00483
00484
00485 _image = glWidget_->grabFrameBuffer(true).copy (_l, _t, _w, _h).convertToFormat (QImage::Format_RGB32);
00486 }
00487
00488
00489
00490
00491
00492 void glViewer::updateGL()
00493 {
00494 if (!properties_.updateLocked() && isVisible() )
00495 {
00496 updatePickCache_ = true;
00497 update();
00498
00499 emit viewUpdated();
00500 }
00501 }
00502
00503
00504
00505
00506
00507
00508 void glViewer::drawScene()
00509 {
00510 QTime timer;
00511 timer.start();
00512
00513
00514
00515
00516
00517
00518 ACG::Vec3d c = glstate_->modelview().transform_point(scene_center_);
00519
00520
00521 far_ = std::max(0.0002f * scene_radius_, -(c[2] - scene_radius_));
00522
00523
00524 near_ = std::max(0.0001f * scene_radius_, -(c[2] + scene_radius_));
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539 updateProjectionMatrix();
00540
00541
00542 glstate_->set_msSinceLastRedraw (redrawTime_.restart ());
00543
00544
00545 makeCurrent();
00546 if (stereo_) drawScene_stereo();
00547 else drawScene_mono();
00548
00549 glFinish();
00550 frame_time_ = timer.elapsed();
00551
00552
00553 }
00554
00555
00556
00557
00558
00559 void glViewer::drawScene_mono()
00560 {
00561 if (sceneGraphRoot_)
00562 {
00563 if (! properties_.renderPicking() ) {
00564
00565 ViewObjectMarker *oM = properties_.objectMarker();
00566 GLuint refBits = 0;
00567 QSet<GLuint> references;
00568
00569 if (oM)
00570 {
00571 glClear (GL_STENCIL_BUFFER_BIT);
00572 glEnable (GL_STENCIL_TEST);
00573 glStencilOp (GL_KEEP, GL_KEEP, GL_ZERO);
00574 glStencilFunc (GL_ALWAYS, 0, ~0);
00575
00576 for (PluginFunctions::ObjectIterator o_it(PluginFunctions::ALL_OBJECTS, DATA_ALL) ;
00577 o_it != PluginFunctions::objectsEnd(); ++o_it)
00578 {
00579 bool ok;
00580 GLuint ref;
00581
00582 ok = oM->stencilRefForObject(*o_it, ref);
00583
00584 if (ok)
00585 {
00586 o_it->stencilRefNode ()->setReference (ref);
00587 o_it->stencilRefNode ()->show ();
00588 refBits |= ref;
00589 references << ref;
00590 }
00591 else
00592 o_it->stencilRefNode ()->hide ();
00593 }
00594 }
00595
00596 ACG::SceneGraph::DrawAction action( properties_.drawMode() , false);
00597 ACG::SceneGraph::traverse(sceneGraphRoot_, action, *glstate_, properties_.drawMode() );
00598
00599 if( blending_ )
00600 {
00601 ACG::SceneGraph::DrawAction action(properties_.drawMode(), true);
00602 ACG::SceneGraph::traverse(sceneGraphRoot_, action, *glstate_, properties_.drawMode());
00603 }
00604
00605 if (oM)
00606 {
00607 if (oM->type() == ViewObjectMarker::PerBit)
00608 {
00609 references.clear ();
00610 for (unsigned int i = 0; i < sizeof (GLuint) * 8; i++)
00611 if (refBits & (1 << i))
00612 references << (1 << i);
00613 }
00614
00615 glPushAttrib(GL_ALL_ATTRIB_BITS);
00616
00617 glEnable(GL_BLEND);
00618 glDisable(GL_DEPTH_TEST);
00619 glDisable(GL_LIGHTING);
00620 glDisable(GL_DITHER);
00621
00622 int vp_l, vp_b, vp_w, vp_h;
00623 glstate_->get_viewport (vp_l, vp_b, vp_w, vp_h);
00624
00625 glMatrixMode(GL_PROJECTION);
00626 glPushMatrix ();
00627 glLoadIdentity();
00628 glOrtho(0, vp_w, vp_h, 0, 0, 1.0);
00629 glMatrixMode(GL_MODELVIEW);
00630 glPushMatrix ();
00631 glLoadIdentity();
00632
00633 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
00634
00635 foreach (unsigned int ref, references)
00636 {
00637 bool ok;
00638 GLenum sfactor;
00639 GLenum dfactor;
00640 ACG::Vec4f color;
00641 unsigned int mask = ~0;
00642
00643 if (oM->type() == ViewObjectMarker::PerBit)
00644 {
00645 ok = oM->blendForStencilRefBit (ref, sfactor, dfactor, color);
00646 mask = ref;
00647 }
00648 else
00649 ok = oM->blendForStencilRefNumber (ref, sfactor, dfactor, color);
00650
00651 if (!ok)
00652 continue;
00653
00654 glStencilFunc (GL_EQUAL, ref, mask);
00655
00656 glBlendFunc (sfactor, dfactor);
00657 glColor4f (color[0], color [1], color [2], color[3]);
00658
00659 glBegin (GL_QUADS);
00660 glVertex2i(0, 0);
00661 glVertex2i(0, vp_h);
00662 glVertex2i(vp_w, vp_h);
00663 glVertex2i(vp_w, 0);
00664 glEnd ();
00665
00666 }
00667
00668 glMatrixMode(GL_PROJECTION);
00669 glPopMatrix ();
00670 glMatrixMode(GL_MODELVIEW);
00671 glPopMatrix ();
00672
00673 glPopAttrib ();
00674 glDisable (GL_STENCIL_TEST);
00675 }
00676 } else {
00677
00678
00679 makeCurrent();
00680
00681 glDisable(GL_LIGHTING);
00682 glDisable(GL_BLEND);
00683 glClear(GL_DEPTH_BUFFER_BIT);
00684 glInitNames();
00685 glPushName((GLuint) 0);
00686
00687
00688 glstate_->pick_init (true);
00689 ACG::SceneGraph::PickAction action(*glstate_, properties_.renderPickingMode(), properties_.drawMode());
00690 ACG::SceneGraph::traverse(sceneGraphRoot_, action);
00691
00692 glEnable(GL_LIGHTING);
00693 glEnable(GL_BLEND);
00694 }
00695 }
00696
00697 if (cursorPainter_ && cursorPainter_->enabled () && cursorPositionValid_)
00698 {
00699 glstate_->push_modelview_matrix ();
00700
00701 glstate_->reset_modelview ();
00702
00703 glstate_->translate (cursorPoint3D_[0], cursorPoint3D_[1], cursorPoint3D_[2]);
00704
00705 cursorPainter_->paintCursor (glstate_);
00706 glstate_->pop_modelview_matrix ();
00707 }
00708
00709 draw_lights();
00710 }
00711
00712
00713
00714
00715
00716 void
00717 glViewer::drawScene_stereo()
00718 {
00719 if (OpenFlipper::Options::stereoMode () == OpenFlipper::Options::OpenGL && OpenFlipper::Options::glStereo ())
00720 {
00721 drawScene_glStereo ();
00722 return;
00723 }
00724 else if (OpenFlipper::Options::stereoMode () == OpenFlipper::Options::AnaglyphCustom && customAnaglyphSupported_)
00725 {
00726 drawScene_customAnaglyphStereo ();
00727
00728
00729 if (customAnaglyphSupported_)
00730 return;
00731 } else if (OpenFlipper::Options::stereoMode () == OpenFlipper::Options::Philips )
00732 {
00733 drawScenePhilipsStereo ();
00734
00735 return;
00736
00737 }
00738
00739
00740
00741 drawScene_anaglyphStereo ();
00742 }
00743
00744
00745
00746
00747
00748 void glViewer::setHome()
00749 {
00750 home_modelview_ = glstate_->modelview();
00751 home_inverse_modelview_ = glstate_->inverse_modelview();
00752 homeOrthoWidth_ = orthoWidth_;
00753 home_center_ = trackball_center_;
00754 home_radius_ = trackball_radius_;
00755 }
00756
00757
00758 void glViewer::home()
00759 {
00760 makeCurrent();
00761 glstate_->set_modelview(home_modelview_, home_inverse_modelview_);
00762 orthoWidth_ = homeOrthoWidth_;
00763 trackball_center_ = home_center_;
00764 trackball_radius_ = home_radius_;
00765 updateProjectionMatrix();
00766 updateGL();
00767
00768 emit viewChanged();
00769
00770 }
00771
00772
00773
00774
00775
00776 void glViewer::viewAll()
00777 {
00778 makeCurrent();
00779
00780
00781 properties_.lockUpdate();
00782
00783
00784 translate(-(glstate_->modelview().transform_point(scene_center_))
00785 - ACG::Vec3d(0.0, 0.0, 3.0 * scene_radius_));
00786
00787 orthoWidth_ = 1.1 * scene_radius_;
00788 double aspect = (double) glWidth() / (double) glHeight();
00789 if (aspect > 1.0)
00790 orthoWidth_ *= aspect;
00791
00792 sceneGraph(PluginFunctions::getSceneGraphRootNode(), true);
00793
00794 properties_.unLockUpdate();
00795 updateProjectionMatrix();
00796 updateGL();
00797
00798 emit viewChanged();
00799
00800 }
00801
00802
00803
00804
00805
00806 void glViewer::flyTo(const QPoint& _pos, bool _move_back)
00807 {
00808 makeCurrent();
00809
00810 unsigned int nodeIdx, targetIdx;
00811 ACG::Vec3d hitPoint;
00812
00813 if (pick( ACG::SceneGraph::PICK_ANYTHING, _pos, nodeIdx, targetIdx, &hitPoint))
00814 {
00815 if (projectionMode_ == PERSPECTIVE_PROJECTION)
00816 {
00817 ACG::Vec3d eye(glstate_->eye());
00818 ACG::Vec3d t = hitPoint - eye;
00819 ACG::Vec3d e = eye + t * (_move_back ? -0.5f : 0.5f);
00820 flyTo(e, hitPoint, 300);
00821 }
00822 else
00823 {
00824
00825 orthoWidth_ *= _move_back ? 2.0 : 0.5;
00826
00827
00828
00829 trackball_center_ = hitPoint;
00830
00832
00833
00834 updateProjectionMatrix();
00835
00836
00837 updateGL();
00838
00839 emit viewChanged();
00840 }
00841
00842
00843 }
00844 }
00845
00846
00847 void glViewer::flyTo(const ACG::Vec3d& _position,
00848 const ACG::Vec3d& _center,
00849 double _time)
00850 {
00851 makeCurrent();
00852
00853
00854 ACG::Vec3d c = glstate_->modelview().transform_point(_center);
00855 ACG::Vec3d p = glstate_->modelview().transform_point(_position);
00856 ACG::Vec3d view =(p-c).normalize();
00857 ACG::Vec3d z(0,0,1);
00858 ACG::Vec3d axis = (z % -view).normalize();
00859 double angle = acos(std::max(-1.0,
00860 std::min(1.0,
00861 (z | view)))) / M_PI * 180.0;
00862
00863 if (angle > 175)
00864 axis = ACG::Vec3d(0,1,0);
00865
00866
00867
00868 ACG::Vec3d target = glstate_->modelview().transform_point(_center);
00869 ACG::Vec3d trans ( -target[0],
00870 -target[1],
00871 -target[2] - (_position-_center).norm() );
00872
00873
00874
00875
00876 unsigned int frames = (unsigned int)(_time / frame_time_);
00877 if (frames > 1000) frames=1000;
00878
00879
00880
00881
00882
00883 if (frames > 10)
00884 {
00885 ACG::Vec3d t = trans / (double)frames;
00886 double a = angle / (double)frames;
00887
00888 for (unsigned int i=0; i<frames; ++i)
00889 {
00890 translate(t);
00891 if (fabs(a) > FLT_MIN)
00892 rotate(axis, a, _center);
00893
00894 update();
00895 qApp->processEvents();
00896 }
00897 updatePickCache_ = true;
00898 }
00899
00900
00901
00902 else
00903 {
00904 translate(trans);
00905 if (fabs(angle) > FLT_MIN)
00906 rotate(axis, angle, _center);
00907
00908 updateGL();
00909 }
00910
00911
00912 trackball_center_ = _center;
00913 trackball_radius_ = std::max(scene_radius_,
00914 (_center-_position).norm()*0.9f);
00915 }
00916
00917
00918
00919
00920
00921 void glViewer::setView(const ACG::GLMatrixd& _modelview,
00922 const ACG::GLMatrixd& _inverse_modelview)
00923 {
00924 makeCurrent();
00925 glstate_->set_modelview(_modelview, _inverse_modelview);
00926 updateGL();
00927
00928 emit viewChanged();
00929 }
00930
00931
00932
00933
00934
00935 void glViewer::initializeGL()
00936 {
00937
00938
00939 properties_.lockUpdate();
00940
00941
00942 glstate_->initialize();
00943
00944
00945 glEnable(GL_DEPTH_TEST);
00946 glEnable(GL_LIGHTING);
00947 glDisable(GL_DITHER);
00948 glShadeModel( GL_FLAT );
00949
00950
00951 projectionMode( projectionMode_ );
00952 normalsMode( normalsMode_ );
00953
00954
00955 applyProperties();
00956
00957
00958 light_matrix_.identity();
00959 update_lights();
00960
00961
00962
00963 scene_center_ = trackball_center_ = ACG::Vec3d( 0.0, 0.0, 0.0 );
00964 scene_radius_ = trackball_radius_ = 1.0;
00965 orthoWidth_ = 2.0;
00966
00967
00968
00969 glstate_->translate(0.0, 0.0, -3.0);
00970 setHome();
00971
00972
00973
00974 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
00975 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
00976 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
00977 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00978 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
00979 glPixelStorei(GL_PACK_SKIP_ROWS, 0);
00980 glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
00981 glPixelStorei(GL_PACK_ALIGNMENT, 1);
00982
00983
00984
00985 properties_.unLockUpdate();
00986
00987 customAnaglyphSupported_ = glewIsSupported("GL_ARB_fragment_program") &&
00988 (glewIsSupported("GL_ARB_texture_rectangle") ||
00989 glewIsSupported("GL_EXT_texture_rectangle") ||
00990 glewIsSupported("GL_NV_texture_rectangle"));
00991
00992 initialized_ = true;
00993
00994 if (sceneGraphRoot_)
00995 {
00996 sceneGraph(sceneGraphRoot_, true);
00997 viewAll ();
00998 }
00999 }
01000
01001
01002
01003
01007 void glViewer::draw_lights() {
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039 }
01040
01041 void glViewer::update_lights()
01042 {
01043 makeCurrent();
01044
01045 glMatrixMode(GL_MODELVIEW);
01046 glPushMatrix();
01047 glLoadIdentity();
01048 glMultMatrixd(light_matrix_.data());
01049
01050 GLfloat pos[4], col[4];
01051
01052 col[0] = col[1] = col[2] = 0.7;
01053 pos[3] = col[3] = 0.0;
01054
01055 #define SET_LIGHT(i,x,y,z) { \
01056 pos[0]=x; pos[1]=y; pos[2]=z; \
01057 glLightfv(GL_LIGHT##i, GL_POSITION, pos); \
01058 glLightfv(GL_LIGHT##i, GL_DIFFUSE, col); \
01059 glLightfv(GL_LIGHT##i, GL_SPECULAR, col); \
01060 glEnable(GL_LIGHT##i); \
01061 }
01062
01063 SET_LIGHT(0, 0.0, 0.0, 1.0);
01064 SET_LIGHT(1, -1.0, 1.0, 0.7);
01065 SET_LIGHT(2, 1.0, 1.0, 0.7);
01066
01067 col[0] = col[1] = col[2] = 0.3; col[3] = 1.0;
01068 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
01069
01070 glPopMatrix();
01071 }
01072
01073
01074 void glViewer::rotate_lights(ACG::Vec3d& _axis, double _angle)
01075 {
01076 light_matrix_.rotate(_angle, _axis[0], _axis[1], _axis[2], ACG::MULT_FROM_LEFT);
01077 update_lights();
01078 }
01079
01080
01081
01082
01083
01084 void glViewer::paintGL()
01085 {
01086 if (!initialized_)
01087 initializeGL ();
01088
01089 if (!properties_.updateLocked())
01090 {
01091 ACG::Vec4f clear_color;
01092
01093 properties_.lockUpdate();
01094
01095 glPushAttrib (GL_ALL_ATTRIB_BITS);
01096
01097 glEnable(GL_DEPTH_TEST);
01098 glEnable(GL_LIGHTING);
01099 glDisable(GL_DITHER);
01100 glShadeModel( GL_FLAT );
01101
01102 glMatrixMode(GL_PROJECTION);
01103 glPushMatrix();
01104
01105 glMatrixMode(GL_MODELVIEW);
01106 glPushMatrix();
01107
01108
01109 normalsMode( normalsMode_ );
01110
01111 applyProperties();
01112
01113
01114 update_lights();
01115
01116 glstate_->setState ();
01117
01118 glColor4f(1.0,0.0,0.0,1.0);
01119
01120 if (properties_.renderPicking())
01121 {
01122 clear_color = properties_.glState().clear_color();
01123 properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 1.0));
01124 }
01125
01126
01127 if (!stereo_)
01128 glstate_->clearBuffers ();
01129
01130 properties_.unLockUpdate();
01131
01132
01133 drawScene();
01134
01135 glPopMatrix();
01136
01137 glMatrixMode(GL_PROJECTION);
01138 glPopMatrix();
01139
01140 glPopAttrib ();
01141
01142 if (properties_.renderPicking())
01143 properties_.glState().set_clear_color (clear_color);
01144 }
01145 }
01146
01147
01148
01149
01150
01151 void glViewer::resizeEvent(QGraphicsSceneResizeEvent *)
01152 {
01153 updateProjectionMatrix();
01154 glstate_->viewport(scenePos().x(),
01155 scene()->height () - scenePos().y() - size().height (),
01156 size().width (), size().height (),
01157 scene()->width (), scene()->height ());
01158 update();
01159
01160 emit viewChanged();
01161 }
01162
01163 void glViewer::moveEvent (QGraphicsSceneMoveEvent *)
01164 {
01165 glstate_->viewport(scenePos().x(),
01166 scene()->height () - scenePos().y() - size().height (),
01167 size().width (), size().height (),
01168 scene()->width (), scene()->height ());
01169 update();
01170
01171 emit viewChanged();
01172 }
01173
01174
01175
01176 void glViewer::encodeView(QString& _view)
01177 {
01178
01179 const ACG::GLMatrixd m = glstate_->modelview();
01180 const ACG::GLMatrixd p = glstate_->projection();
01181
01182
01183 _view += QString(VIEW_MAGIC) + "\n";
01184 _view += QString::number(m(0,0)) + " " + QString::number(m(0,1)) + " " + QString::number(m(0,2)) + " " + QString::number(m(0,3)) + "\n";
01185 _view += QString::number(m(1,0)) + " " + QString::number(m(1,1)) + " " + QString::number(m(1,2)) + " " + QString::number(m(1,3)) + "\n";
01186 _view += QString::number(m(2,0)) + " " + QString::number(m(2,1)) + " " + QString::number(m(2,2)) + " " + QString::number(m(2,3)) + "\n";
01187 _view += QString::number(m(3,0)) + " " + QString::number(m(3,1)) + " " + QString::number(m(3,2)) + " " + QString::number(m(3,3)) + "\n";
01188
01189
01190 _view += QString::number(p(0,0)) + " " + QString::number(p(0,1)) + " " + QString::number(p(0,2)) + " " + QString::number(p(0,3)) + "\n";
01191 _view += QString::number(p(1,0)) + " " + QString::number(p(1,1)) + " " + QString::number(p(1,2)) + " " + QString::number(p(1,3)) + "\n";
01192 _view += QString::number(p(2,0)) + " " + QString::number(p(2,1)) + " " + QString::number(p(2,2)) + " " + QString::number(p(2,3)) + "\n";
01193 _view += QString::number(p(3,0)) + " " + QString::number(p(3,1)) + " " + QString::number(p(3,2)) + " " + QString::number(p(3,3)) + "\n";
01194
01195
01196 _view += QString::number(glWidth()) + " " + QString::number(glHeight()) + " " + QString::number(projectionMode_) + " " + QString::number(orthoWidth_) + "\n";
01197 }
01198
01199
01200
01201
01202
01203 bool glViewer::decodeView(const QString& _view)
01204 {
01205 if (_view.left(sizeof(VIEW_MAGIC)-1) != QString(VIEW_MAGIC))
01206 return false;
01207
01208
01209 QString temp = _view;
01210 temp.remove(0,sizeof(VIEW_MAGIC));
01211
01212
01213 QStringList split = temp.split(QRegExp("[\\n\\s]"),QString::SkipEmptyParts);
01214
01215 ACG::GLMatrixd m, p;
01216 int w, h, pMode;
01217
01218
01219 if ( split.size() != 36 ) {
01220 std::cerr << "Unable to paste view ... wrong parameter count!! is" << split.size() << std::endl;
01221 return false;
01222 }
01223
01224
01225 bool ok = true;;
01226
01227 m(0,0) = split[0].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01228 m(0,1) = split[1].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01229 m(0,2) = split[2].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01230 m(0,3) = split[3].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01231 m(1,0) = split[4].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01232 m(1,1) = split[5].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01233 m(1,2) = split[6].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01234 m(1,3) = split[7].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01235 m(2,0) = split[8].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01236 m(2,1) = split[9].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01237 m(2,2) = split[10].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01238 m(2,3) = split[11].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01239 m(3,0) = split[12].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01240 m(3,1) = split[13].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01241 m(3,2) = split[14].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01242 m(3,3) = split[15].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01243 p(0,0) = split[16].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01244 p(0,1) = split[17].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01245 p(0,2) = split[18].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01246 p(0,3) = split[19].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01247 p(1,0) = split[20].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01248 p(1,1) = split[21].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01249 p(1,2) = split[22].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01250 p(1,3) = split[23].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01251 p(2,0) = split[24].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01252 p(2,1) = split[25].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01253 p(2,2) = split[26].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01254 p(2,3) = split[27].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01255 p(3,0) = split[28].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01256 p(3,1) = split[29].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01257 p(3,2) = split[30].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01258 p(3,3) = split[31].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01259
01260 w = split[32].toInt(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01261 h = split[33].toInt(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01262 pMode = split[34].toInt(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01263 orthoWidth_ = split[35].toDouble(&ok); if ( !ok ) { std::cerr << "Error in decoding View!" << std::endl; return false; }
01264
01265
01266 makeCurrent();
01267
01268
01269 if (projectionMode_ != (ProjectionMode)pMode)
01270 projectionMode((ProjectionMode)pMode);
01271
01272
01273 glstate_->set_modelview(m);
01274
01275
01276 std::cerr << "Todo : Add Checkbox if size should also be pasted" << std::endl;
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286 updateGL();
01287
01288 return true;
01289 }
01290
01291
01292
01293
01294
01295 void glViewer::actionCopyView()
01296 {
01297 QString view; encodeView(view);
01298 QApplication::clipboard()->setText(view);
01299 }
01300
01301
01302
01303
01304
01305 void glViewer::actionPasteView()
01306 {
01307 QString view; view=QApplication::clipboard()->text();
01308 decodeView(view);
01309 }
01310
01311
01312
01313
01314 void
01315 glViewer::createWidgets()
01316 {
01317
01318
01319 wheelZ_=new ACG::QtWidgets::QtWheel( 0,"wheel-z",ACG::QtWidgets::QtWheel::Vertical);
01320 wheelZ_->setMinimumSize(wheelZ_->sizeHint());
01321 wheelZ_->setMaximumSize(wheelZ_->sizeHint());
01322 connect(wheelZ_,SIGNAL(angleChangedBy(double)),
01323 this,SLOT(slotWheelZ(double)));
01324 wheelZ_->setToolTip( tr("Translate along <b>z-axis</b>."));
01325 wheelZ_->setWhatsThis( tr("Translate along <b>z-axis</b>."));
01326
01327 wheelY_=new ACG::QtWidgets::QtWheel( 0,"wheel-y",ACG::QtWidgets::QtWheel::Horizontal);
01328 wheelY_->setMinimumSize(wheelY_->sizeHint());
01329 wheelY_->setMaximumSize(wheelY_->sizeHint());
01330 connect(wheelY_,SIGNAL(angleChangedBy(double)),
01331 this,SLOT(slotWheelY(double)));
01332 wheelY_->setToolTip(tr("Rotate around <b>y-axis</b>."));
01333 wheelY_->setWhatsThis( tr("Rotate around <b>y-axis</b>."));
01334
01335 wheelX_=new ACG::QtWidgets::QtWheel( 0,"wheel-x" ,ACG::QtWidgets::QtWheel::Vertical);
01336 wheelX_->setMinimumSize(wheelX_->sizeHint());
01337 wheelX_->setMaximumSize(wheelX_->sizeHint());
01338 connect(wheelX_,SIGNAL(angleChangedBy(double)),
01339 this,SLOT(slotWheelX(double)));
01340 wheelX_->setToolTip(tr("Rotate around <b>x-axis</b>."));
01341 wheelX_->setWhatsThis( tr("Rotate around <b>x-axis</b>."));
01342
01343
01344 if(!OpenFlipper::Options::showWheelsAtStartup()) {
01345
01346 slotHideWheels();
01347 }
01348
01349 QGraphicsWidget *wheelX = glScene_->addWidget (wheelX_);
01350 QGraphicsWidget *wheelY = glScene_->addWidget (wheelY_);
01351 QGraphicsWidget *wheelZ = glScene_->addWidget (wheelZ_);
01352
01353 wheelX_->setWindowOpacity (0.5);
01354 wheelY_->setWindowOpacity (0.5);
01355 wheelZ_->setWindowOpacity (0.5);
01356
01357 wheelX->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
01358 wheelY->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
01359 wheelZ->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
01360
01361 glBaseLayout_ = new QtGLViewerLayout;
01362 glBaseLayout_->addWheelX(wheelX);
01363 glBaseLayout_->addWheelY(wheelY);
01364 glBaseLayout_->addWheelZ(wheelZ);
01365
01366 connect(wheelX_,SIGNAL(hideWheel()),this,SLOT(slotHideWheels()));
01367 connect(wheelY_,SIGNAL(hideWheel()),this,SLOT(slotHideWheels()));
01368 connect(wheelZ_,SIGNAL(hideWheel()),this,SLOT(slotHideWheels()));
01369
01370 setLayout(glBaseLayout_);
01371 }
01372
01373
01374
01375
01376 void glViewer::translate(const ACG::Vec3d& _trans)
01377 {
01378 makeCurrent();
01379 glstate_->translate(_trans[0], _trans[1], _trans[2], ACG::MULT_FROM_LEFT);
01380
01381 emit viewChanged();
01382 }
01383
01384
01385
01386
01387
01388 void glViewer::initModelviewMatrix()
01389 {
01390 makeCurrent();
01391 glstate_->reset_modelview();
01392 }
01393
01394
01395
01396
01397
01398 void glViewer::rotate(const ACG::Vec3d& _axis,
01399 double _angle,
01400 const ACG::Vec3d& _center)
01401 {
01402 makeCurrent();
01403 ACG::Vec3d t = glstate_->modelview().transform_point(_center);
01404 glstate_->translate(-t[0], -t[1], -t[2], ACG::MULT_FROM_LEFT);
01405 glstate_->rotate(_angle, _axis[0], _axis[1], _axis[2], ACG::MULT_FROM_LEFT);
01406 glstate_->translate( t[0], t[1], t[2], ACG::MULT_FROM_LEFT);
01407
01408 emit viewChanged();
01409 }
01410
01411
01412
01413
01414
01415 unsigned int glViewer::glWidth() const {
01416 return size().width();
01417 }
01418 unsigned int glViewer::glHeight() const {
01419 return size().height();
01420 }
01421 QSize glViewer::glSize() const {
01422 return QSize(size().width(),size().height());
01423 }
01424 QPoint glViewer::glMapFromGlobal( const QPoint& _pos ) const {
01425 QPoint p (scene()->views().front()->mapFromGlobal(_pos));
01426 QPointF f (mapFromScene(QPointF(p.x(), p.y ())));
01427 return QPoint (f.x(), f.y());
01428 }
01429
01430 QPoint glViewer::glMapToGlobal( const QPoint& _pos ) const {
01431 QPointF f (mapToScene(QPointF(_pos.x(), _pos.y ())));
01432 QPoint p (f.x(), f.y());
01433 return scene()->views().front()->mapToGlobal(p);
01434 }
01435
01436
01437
01438
01439 void glViewer::slotWheelX(double _dAngle)
01440 {
01441 rotate(ACG::Vec3d(1,0,0),ACG::QtWidgets::QtWheel::deg(ACG::QtWidgets::QtWheel::clip(_dAngle)));
01442 updateGL();
01443
01444 emit viewChanged();
01445 }
01446
01447 void glViewer::slotWheelY(double _dAngle)
01448 {
01449 rotate(ACG::Vec3d(0,1,0),ACG::QtWidgets::QtWheel::deg(ACG::QtWidgets::QtWheel::clip(_dAngle)));
01450 updateGL();
01451
01452 emit viewChanged();
01453 }
01454
01455 void glViewer::slotWheelZ(double _dist)
01456 {
01457 double dz=_dist*0.5/M_PI*scene_radius_*2.0;
01458 translate(ACG::Vec3d(0,0,dz));
01459 updateGL();
01460
01461 emit viewChanged();
01462 }
01463
01464
01465
01466
01467 void glViewer::grabGLArea()
01468 {
01469 glareaGrabbed_ = true;
01470
01471 if (cursorPainter_)
01472 cursorPainter_->setCursor(Qt::BlankCursor);
01473 else
01474 setCursor(Qt::BlankCursor);
01475 grabMouse();
01476 grabKeyboard();
01477 }
01478
01479 void glViewer::releaseGLArea()
01480 {
01481 glareaGrabbed_ = false;
01482
01483 ungrabMouse();
01484 ungrabKeyboard();
01485
01486 if (cursorPainter_)
01487 cursorPainter_->setCursor(Qt::ArrowCursor);
01488 else
01489 setCursor(Qt::ArrowCursor);
01490 }
01491
01492
01493
01494
01495
01496 void glViewer::contextMenuEvent(QGraphicsSceneContextMenuEvent* _e)
01497 {
01498 QPoint p (_e->pos().x(), _e->pos().y());
01499 emit signalCustomContextMenuRequested (p);
01500 }
01501
01502
01503
01504
01505 void glViewer::mousePressEvent(QGraphicsSceneMouseEvent* _e)
01506 {
01507 QPoint p (_e->scenePos().x(), _e->scenePos().y());
01508 QMouseEvent me(QEvent::MouseButtonPress ,p, _e->screenPos(), _e->button(),
01509 _e->buttons(), _e->modifiers());
01510 _e->accept ();
01511
01512 emit signalMakeActive();
01513 glScene_->update ();
01514
01515
01516 if (_e->button() != Qt::RightButton )
01517 {
01518 switch (properties_.actionMode())
01519 {
01520 case Viewer::ExamineMode:
01521 if ((_e->modifiers() & Qt::ControlModifier))
01522 emit startDragEvent( &me );
01523 else
01524 viewMouseEvent(&me);
01525
01526 if (clickTimer_.isActive ())
01527 {
01528 clickTime_ = QTime ();
01529 clickTimer_.stop ();
01530 }
01531 else
01532 {
01533 clickTime_.start ();
01534 clickEvent_ = me;
01535 }
01536 break;
01537
01538 case Viewer::LightMode:
01539 lightMouseEvent(&me);
01540 break;
01541
01542 case Viewer::PickingMode:
01543 emit(signalMouseEvent(&me, properties_.pickMode() ));
01544 emit(signalMouseEvent(&me));
01545 break;
01546
01547 case Viewer::QuestionMode:
01548 emit(signalMouseEventIdentify(&me));
01549 break;
01550 }
01551 }
01552 }
01553
01554
01555
01556
01557
01558 void glViewer::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* _e)
01559 {
01560 QPoint p (_e->scenePos().x(), _e->scenePos().y());
01561 QMouseEvent me(QEvent::MouseButtonDblClick ,p, _e->screenPos(), _e->button(),
01562 _e->buttons(), _e->modifiers());
01563 _e->accept ();
01564
01565 emit signalMakeActive();
01566 glScene_->update ();
01567
01568 switch (properties_.actionMode())
01569 {
01570 case Viewer::ExamineMode:
01571 viewMouseEvent(&me);
01572 emit signalMouseEventClick (&me, true);
01573 break;
01574
01575 case Viewer::LightMode:
01576 lightMouseEvent(&me);
01577 break;
01578
01579 case Viewer::PickingMode:
01580 emit(signalMouseEvent(&me, properties_.pickMode() ));
01581 emit(signalMouseEvent(&me));
01582 break;
01583
01584 case Viewer::QuestionMode:
01585 emit(signalMouseEventIdentify(&me));
01586 break;
01587 }
01588 }
01589
01590
01591
01592
01593
01594 void glViewer::mouseMoveEvent(QGraphicsSceneMouseEvent* _e)
01595 {
01596 QPoint p (_e->scenePos().x(), _e->scenePos().y());
01597 QMouseEvent me(QEvent::MouseMove ,p, _e->screenPos(), _e->button(),
01598 _e->buttons(), _e->modifiers());
01599 _e->accept();
01600
01601 switch ( properties_.actionMode() )
01602 {
01603 case Viewer::ExamineMode:
01604 viewMouseEvent(&me);
01605 break;
01606
01607 case Viewer::LightMode:
01608 lightMouseEvent(&me);
01609 break;
01610
01611 case Viewer::PickingMode:
01612
01613
01614 if ((_e->buttons() & (Qt::LeftButton | Qt::MidButton | Qt::RightButton))
01615 || trackMouse_)
01616 {
01617 emit(signalMouseEvent(&me, properties_.pickMode() ));
01618 emit(signalMouseEvent(&me));
01619 }
01620 break;
01621
01622 case Viewer::QuestionMode:
01623 emit(signalMouseEventIdentify(&me));
01624 break;
01625
01626 default:
01627 break;
01628 }
01629 }
01630
01631
01632
01633
01634 void glViewer::mouseReleaseEvent(QGraphicsSceneMouseEvent* _e)
01635 {
01636 QPoint p (_e->scenePos().x(), _e->scenePos().y());
01637 QMouseEvent me(QEvent::MouseButtonRelease ,p, _e->screenPos(), _e->button(),
01638 _e->buttons(), _e->modifiers());
01639 _e->accept();
01640
01641
01642
01643
01644 if (_e->button() != Qt::RightButton || (properties_.actionMode() == Viewer::PickingMode) )
01645 {
01646 switch ( properties_.actionMode() )
01647 {
01648 case Viewer::ExamineMode:
01649 viewMouseEvent(&me);
01650
01651 if (!clickTime_.isNull ())
01652 {
01653 int elapsed = clickTime_.elapsed ();
01654 QPoint diff = clickEvent_.pos () - me.pos ();
01655
01656 if (abs (diff.x ()) <= 1 && abs (diff.y ()) <= 1 && elapsed <= QApplication::doubleClickInterval () / 2)
01657 {
01658 clickTimer_.setSingleShot (true);
01659 clickTimer_.setInterval (QApplication::doubleClickInterval () - elapsed);
01660 clickTimer_.start ();
01661 }
01662 else
01663 {
01664 clickTime_ = QTime ();
01665 clickTimer_.stop ();
01666 }
01667 }
01668 break;
01669
01670 case Viewer::LightMode:
01671 lightMouseEvent(&me);
01672 break;
01673
01674 case Viewer::PickingMode:
01675 emit(signalMouseEvent(&me, properties_.pickMode() ));
01676 emit(signalMouseEvent(&me));
01677 break;
01678
01679 case Viewer::QuestionMode:
01680 emit(signalMouseEventIdentify(&me));
01681 break;
01682
01683 default:
01684 break;
01685 }
01686 }
01687
01688 isRotating_ = false;
01689 }
01690
01691
01692
01693
01694
01695 void glViewer::wheelEvent(QGraphicsSceneWheelEvent* _e)
01696 {
01697 QPoint p (_e->scenePos().x(), _e->scenePos().y());
01698 QWheelEvent we(p, _e->screenPos(), _e->delta(), _e->buttons(),
01699 _e->modifiers(), _e->orientation());
01700 _e->accept();
01701
01702 switch ( properties_.actionMode() )
01703 {
01704 case Viewer::ExamineMode:
01705 viewWheelEvent(&we);
01706 break;
01707
01708 case Viewer::PickingMode:
01709 emit(signalWheelEvent(&we, properties_.pickMode() ));
01710 break;
01711
01712 default:
01713 break;
01714 }
01715
01716 isRotating_ = false;
01717 }
01718
01719
01720
01721 void glViewer::dragEnterEvent(QGraphicsSceneDragDropEvent* _e)
01722 {
01723 std::cerr << "dragEnter" << std::endl;
01724
01725 QPoint p (_e->pos().x(), _e->pos().y());
01726 QDragEnterEvent de(p, _e->possibleActions(), _e->mimeData(), _e->buttons(),
01727 _e->modifiers ());
01728 emit dragEnterEvent(&de);
01729 _e->accept();
01730 }
01731
01732
01733
01734
01735 void glViewer::dropEvent(QGraphicsSceneDragDropEvent* _e)
01736 {
01737 std::cerr << "drop" << std::endl;
01738
01739 QPoint p (_e->pos().x(), _e->pos().y());
01740 QDropEvent de(p, _e->possibleActions(), _e->mimeData(), _e->buttons(),
01741 _e->modifiers ());
01742 emit dropEvent(&de);
01743 _e->accept();
01744 }
01745
01746
01747
01748
01749 void glViewer::viewMouseEvent(QMouseEvent* _event) {
01750
01751 if (navigationMode_ == NORMAL_NAVIGATION) {
01752
01753 handleNormalNavigation(_event);
01754
01755 } else if (navigationMode_ == FIRSTPERSON_NAVIGATION) {
01756
01757 handleFirstPersonNavigation(_event);
01758 }
01759
01760 }
01761
01762
01763
01764 void glViewer::handleFirstPersonNavigation( QMouseEvent* _event) {
01765
01766
01767 QPointF f(mapFromScene(QPointF(_event->pos().x(), _event->pos().y())));
01768 QPoint pos(f.x(), f.y());
01769
01770 switch (_event->type()) {
01771
01772 case QEvent::MouseButtonPress: {
01773 lastPoint2D_ = pos;
01774 lookAround_ = true;
01775 break;
01776 }
01777
01778 case QEvent::MouseButtonDblClick: {
01779 if (allowRotation_)
01780 flyTo(_event->pos(), _event->button() == Qt::MidButton);
01781 break;
01782 }
01783
01784 case QEvent::MouseMove: {
01785
01786 if(!lookAround_) break;
01787
01788
01789 QPoint newpos = QPoint(pos.x() - glWidth() / 2, glHeight() / 2 - pos.y());
01790 QPoint oldpos = QPoint(lastPoint2D_.x() - glWidth() / 2, glHeight() / 2 - lastPoint2D_.y());
01791
01792 double x = 2.0 * newpos.x() / glWidth();
01793 double y = 2.0 * newpos.y() / glHeight();
01794
01795 double xo = 2.0 * oldpos.x() / glWidth();
01796 double yo = 2.0 * oldpos.y() / glHeight();
01797
01798 double diffx = xo - x;
01799 double diffy = yo - y;
01800
01801 ACG::Vec3d yaxis = glstate_->modelview().transform_vector(glstate_->up());
01802 ACG::Vec3d xaxis = glstate_->modelview().transform_vector(glstate_->right());
01803
01804 rotate(yaxis, -diffx * 90, glstate_->eye());
01805 rotate(xaxis, diffy * 90, glstate_->eye());
01806
01807 lastPoint2D_ = pos;
01808
01809 updateGL();
01810 lastMoveTime_.restart();
01811
01812 emit viewChanged();
01813
01814 break;
01815 }
01816
01817 case QEvent::MouseButtonRelease: {
01818 lookAround_ = false;
01819 break;
01820 }
01821
01822 default:
01823 break;
01824 }
01825 }
01826
01827
01828
01829 void glViewer::handleNormalNavigation( QMouseEvent* _event ) {
01830
01831
01832 QPointF f(mapFromScene(QPointF(_event->pos().x(), _event->pos().y())));
01833 QPoint pos(f.x(), f.y());
01834
01835 switch (_event->type()) {
01836
01837 case QEvent::MouseButtonPress: {
01838
01839 if (_event->modifiers() & Qt::ShiftModifier) {
01840 ACG::Vec3d c;
01841 if (fast_pick(pos, c)) {
01842 trackball_center_ = c;
01843 trackball_radius_ = std::max(scene_radius_, (c - glstate_->eye()).norm() * 0.9f);
01844 }
01845 }
01846
01847 lastPoint_hitSphere_ = mapToSphere(lastPoint2D_ = pos, lastPoint3D_);
01848 isRotating_ = true;
01849 timer_->stop();
01850
01851 break;
01852 }
01853
01854 case QEvent::MouseButtonDblClick: {
01855 if (allowRotation_)
01856 flyTo(_event->pos(), _event->button() == Qt::MidButton);
01857 break;
01858 }
01859
01860 case QEvent::MouseMove: {
01861 double factor = 1.0;
01862
01863 if (_event->modifiers() == Qt::ShiftModifier)
01864 factor = properties_.wheelZoomFactorShift();
01865
01866
01867 if (_event->buttons() & (Qt::LeftButton | Qt::MidButton)) {
01868 QPoint newPoint2D = pos;
01869
01870 double value_x, value_y;
01871 ACG::Vec3d newPoint3D;
01872 bool newPoint_hitSphere = mapToSphere(newPoint2D, newPoint3D);
01873
01874 makeCurrent();
01875
01876
01877 if ((_event->buttons() & Qt::LeftButton) && (_event->buttons() & Qt::MidButton)) {
01878 switch (projectionMode()) {
01879 case PERSPECTIVE_PROJECTION: {
01880 value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 3.0 / (double) glHeight();
01881 translate(ACG::Vec3d(0.0, 0.0, value_y * factor));
01882 updateGL();
01883 emit viewChanged();
01884 break;
01885 }
01886
01887 case ORTHOGRAPHIC_PROJECTION: {
01888 value_y = ((newPoint2D.y() - lastPoint2D_.y())) * orthoWidth_ / (double) glHeight();
01889 orthoWidth_ -= value_y * factor;
01890 updateProjectionMatrix();
01891 updateGL();
01892 emit viewChanged();
01893 break;
01894 }
01895 }
01896 }
01897
01898
01899 else if ((_event->buttons() & Qt::MidButton) || (!allowRotation_ && (_event->buttons() & Qt::LeftButton))) {
01900 value_x = scene_radius_ * ((newPoint2D.x() - lastPoint2D_.x())) * 2.0 / (double) glWidth();
01901 value_y = scene_radius_ * ((newPoint2D.y() - lastPoint2D_.y())) * 2.0 / (double) glHeight();
01902 translate(ACG::Vec3d(value_x * factor, -value_y * factor, 0.0));
01903 }
01904
01905
01906 else if (allowRotation_ && (_event->buttons() & Qt::LeftButton)) {
01907 ACG::Vec3d axis(1.0, 0.0, 0.0);
01908 double angle(0.0);
01909
01910 if (lastPoint_hitSphere_) {
01911
01912 if ((newPoint_hitSphere = mapToSphere(newPoint2D, newPoint3D))) {
01913 axis = lastPoint3D_ % newPoint3D;
01914 double cos_angle = (lastPoint3D_ | newPoint3D);
01915 if (fabs(cos_angle) < 1.0) {
01916 angle = acos(cos_angle) * 180.0 / M_PI * factor;
01917 angle *= 2.0;
01918 }
01919 }
01920
01921 rotate(axis, angle);
01922
01923 }
01924
01925 lastRotationAxis_ = axis;
01926 lastRotationAngle_ = angle;
01927 }
01928
01929 lastPoint2D_ = newPoint2D;
01930 lastPoint3D_ = newPoint3D;
01931 lastPoint_hitSphere_ = newPoint_hitSphere;
01932
01933 updateGL();
01934 lastMoveTime_.restart();
01935 emit viewChanged();
01936 }
01937 break;
01938 }
01939
01940 case QEvent::MouseButtonRelease: {
01941 lastPoint_hitSphere_ = false;
01942
01943
01944 if (isRotating_ && (_event->button() == Qt::LeftButton) && (!(_event->buttons() & Qt::MidButton))
01945 && (lastMoveTime_.elapsed() < 50) && properties_.animation())
01946 timer_->start(0);
01947 break;
01948 }
01949
01950 default:
01951 break;
01952 }
01953 }
01954
01955
01956
01957 void
01958 glViewer::lightMouseEvent(QMouseEvent* _event)
01959 {
01960 QPointF f (mapFromScene(QPointF(_event->pos().x(), _event->pos().y())));
01961 QPoint pos (f.x(), f.y());
01962
01963 switch (_event->type())
01964 {
01965 case QEvent::MouseButtonPress:
01966 {
01967 lastPoint_hitSphere_ = mapToSphere( lastPoint2D_= pos,
01968 lastPoint3D_ );
01969 isRotating_ = true;
01970 timer_->stop();
01971 break;
01972 }
01973
01974
01975 case QEvent::MouseMove:
01976 {
01977
01978 if (_event->buttons() & Qt::LeftButton)
01979 {
01980 QPoint newPoint2D = pos;
01981
01982 if ( (newPoint2D.x()<0) || (newPoint2D.x() > (int)glWidth()) ||
01983 (newPoint2D.y()<0) || (newPoint2D.y() > (int)glHeight()) )
01984 return;
01985
01986
01987 ACG::Vec3d newPoint3D;
01988 bool newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D );
01989
01990 makeCurrent();
01991
01992 ACG::Vec3d axis(1.0,0.0,0.0);
01993 double angle(0.0);
01994
01995 if ( lastPoint_hitSphere_ )
01996 {
01997 if ( ( newPoint_hitSphere = mapToSphere( newPoint2D, newPoint3D ) ) )
01998 {
01999 axis = lastPoint3D_ % newPoint3D;
02000 double cos_angle = ( lastPoint3D_ | newPoint3D );
02001 if ( fabs(cos_angle) < 1.0 ) {
02002 angle = acos( cos_angle ) * 180.0 / M_PI;
02003 angle *= 2.0;
02004 }
02005 }
02006 rotate_lights(axis, angle);
02007 }
02008
02009 lastPoint2D_ = newPoint2D;
02010 lastPoint3D_ = newPoint3D;
02011 lastPoint_hitSphere_ = newPoint_hitSphere;
02012
02013 updateGL();
02014 }
02015 break;
02016 }
02017
02018
02019 default:
02020 break;
02021 }
02022 }
02023
02024
02025
02026 void glViewer::viewWheelEvent( QWheelEvent* _event)
02027 {
02028 double factor = properties_.wheelZoomFactor();
02029
02030 if (_event->modifiers() == Qt::ShiftModifier)
02031 factor = properties_.wheelZoomFactorShift();
02032
02033 if (projectionMode() == PERSPECTIVE_PROJECTION || stereo_)
02034 {
02035 double d = -(double)_event->delta() / 120.0 * 0.2 * factor * trackball_radius_/3;
02036 translate( ACG::Vec3d(0.0, 0.0, d) );
02037 updateGL();
02038 }
02039 else
02040 {
02041 double d = (double)_event->delta() / 120.0 * 0.2 * factor * orthoWidth_;
02042 orthoWidth_ += d;
02043 updateProjectionMatrix();
02044 updateGL();
02045 }
02046
02047 emit viewChanged();
02048 }
02049
02050
02051
02052
02053
02054 bool glViewer::mapToSphere(const QPoint& _v2D, ACG::Vec3d& _v3D) const
02055 {
02056 if ( (_v2D.x() >= 0) && (_v2D.x() < (int)glWidth()) &&
02057 (_v2D.y() >= 0) && (_v2D.y() < (int)glHeight()) )
02058 {
02059 double x = (double)(_v2D.x() - ((double)glWidth() / 2.0)) / (double)glWidth();
02060 double y = (double)(((double)glHeight() / 2.0) - _v2D.y()) / (double)glHeight();
02061 double sinx = sin(M_PI * x * 0.5);
02062 double siny = sin(M_PI * y * 0.5);
02063 double sinx2siny2 = sinx * sinx + siny * siny;
02064
02065 _v3D[0] = sinx;
02066 _v3D[1] = siny;
02067 _v3D[2] = sinx2siny2 < 1.0 ? sqrt(1.0 - sinx2siny2) : 0.0;
02068
02069 return true;
02070 }
02071 else return false;
02072 }
02073
02074
02075
02076
02077
02078 void glViewer::slotAnimation()
02079 {
02080 static int msecs=0, count=0;
02081
02082 makeCurrent();
02083 rotate( lastRotationAxis_, lastRotationAngle_ );
02084 updateGL();
02085
02086 if (!properties_.updateLocked()) {
02087 msecs += frame_time_;
02088 if (count >= 10 && msecs >= 500) {
02089 char s[100];
02090 sprintf( s, "%.3f fps", (1000.0 * count / (float)msecs) );
02091 emit statusMessage(s,2000);
02092 count = msecs = 0;
02093 }
02094 else
02095 ++count;
02096 }
02097 }
02098
02099 void glViewer::applyProperties() {
02100
02101 glstate_->set_twosided_lighting( properties_.twoSidedLighting() );
02102
02103 glstate_->set_clear_color( properties_.backgroundColor() );
02104
02105 glstate_->allow_multisampling( properties_.multisampling() );
02106
02107 if (properties_.isCCWFront() )
02108 glFrontFace( GL_CCW );
02109 else
02110 glFrontFace( GL_CW );
02111
02112 if ( properties_.backFaceCulling() )
02113 glEnable( GL_CULL_FACE );
02114 else
02115 glDisable( GL_CULL_FACE );
02116
02117 }
02118
02119 void glViewer::slotPropertiesUpdated() {
02120 makeCurrent();
02121 applyProperties();
02122 updateGL();
02123 }
02124
02125 void glViewer::snapshot(QImage& _image, bool _offScreenRendering)
02126 {
02127
02128 if ( _offScreenRendering ){
02129 QGLFramebufferObject fb( glstate_->context_width(), glstate_->context_height(), QGLFramebufferObject::CombinedDepthStencil);
02130
02131 if ( fb.isValid() ){
02132
02133 fb.bind();
02134 qApp->processEvents();
02135 makeCurrent();
02136 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
02137 paintGL();
02138 glFinish();
02139
02140 _image = fb.toImage().copy(scenePos().x(), scenePos().y(), glWidth(), glHeight());
02141
02142 return;
02143 }
02144 }
02145
02146 makeCurrent();
02147
02148 qApp->processEvents();
02149 makeCurrent();
02150 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
02151 paintGL();
02152 glFinish();
02153
02154 copyToImage(_image, scenePos().x(), scenePos().y(), glWidth(), glHeight(), GL_BACK);
02155 }
02156
02157 void glViewer::snapshot()
02158 {
02159 makeCurrent();
02160
02161 qApp->processEvents();
02162 makeCurrent();
02163 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
02164 paintGL();
02165 glFinish();
02166
02167 QImage snapshot;
02168 copyToImage(snapshot, scenePos().x(), scenePos().y(), glWidth(), glHeight(), GL_BACK);
02169
02170 QFileInfo fi(properties_.snapshotName());
02171
02172 QString fname = fi.path() + QDir::separator() +fi.baseName() + "." + QString::number(properties_.snapshotCounter()) + ".";
02173
02174 QString format="png";
02175
02176 if (fi.completeSuffix() == "ppm")
02177 format="ppmraw";
02178
02179 fname += format;
02180
02181 bool rval=snapshot.save(fname,format.toUpper().toLatin1());
02182
02183
02184 if (rval)
02185 {
02186 emit statusMessage (QString(tr("snapshot: "))+fname,5000);
02187 }
02188 else
02189 {
02190 emit statusMessage (QString(tr("could not save snapshot to "))+fname);
02191 }
02192
02193 }
02194
02195 void glViewer::slotHideWheels() {
02196 if (isVisible())
02197 {
02198 wheelX_->hide();
02199 wheelY_->hide();
02200 wheelZ_->hide();
02201 }
02202 else
02203 {
02204 show ();
02205 wheelX_->hide();
02206 wheelY_->hide();
02207 wheelZ_->hide();
02208 hide ();
02209 }
02210 }
02211
02212 void glViewer::slotShowWheels() {
02213 if (isVisible())
02214 {
02215 wheelX_->show();
02216 wheelY_->show();
02217 wheelZ_->show();
02218 }
02219 else
02220 {
02221 show ();
02222 wheelX_->show();
02223 wheelY_->show();
02224 wheelZ_->show();
02225 hide ();
02226 }
02227 }
02228
02229 bool glViewer::wheelsVisible() {
02230
02231 return true;
02232 }
02233
02234
02235
02236 void glViewer::slotClickTimeout()
02237 {
02238 emit signalMouseEventClick (&clickEvent_, false);
02239 }
02240
02241
02242
02243 void glViewer::setCursorPainter (CursorPainter *_cursorPainter)
02244 {
02245 cursorPainter_ = _cursorPainter;
02246 }
02247
02248
02249
02250 void glViewer::updateCursorPosition (QPointF _scenePos)
02251 {
02252 if (!initialized_ || !sceneGraphRoot_ || !isVisible ())
02253 return;
02254
02255 unsigned int nodeIdx, targetIdx;
02256 ACG::Vec3d tmp;
02257
02258
02259 if (!mapRectToScene(boundingRect ()).intersects (cursorPainter_->cursorBoundingBox().translated(_scenePos)))
02260 {
02261 cursorPositionValid_ = false;
02262 }
02263
02264 else if (stereo_ && OpenFlipper::Options::stereoMousePick() &&
02265 pick (ACG::SceneGraph::PICK_ANYTHING, _scenePos.toPoint(), nodeIdx, targetIdx, &tmp))
02266 {
02267
02268 cursorPoint3D_ = glstate_->modelview ().transform_point (tmp);
02269
02270 cursorPositionValid_ = true;
02271 }
02272 else
02273 {
02274 glstate_->push_modelview_matrix ();
02275
02276
02277 glstate_->reset_modelview ();
02278
02279
02280
02281 double zerop = near_ + ((far_ - near_) * OpenFlipper::Options::focalDistance ());
02282 ACG::Vec3d zerod = glstate_->project (ACG::Vec3d (0.0, 0.0, -zerop));
02283
02284
02285 tmp = glstate_->unproject (ACG::Vec3d (_scenePos.x(), scene()->height () - _scenePos.y(), zerod[2]));
02286 cursorPoint3D_ = tmp;
02287 glstate_->pop_modelview_matrix ();
02288 cursorPositionValid_ = true;
02289 }
02290 }
02291
02292
02293
02294
02295 void glViewer::moveForward() {
02296 if(navigationMode_ == FIRSTPERSON_NAVIGATION) {
02297
02298 ACG::Vec3d dir = glstate_->viewing_direction();
02299
02300 dir *= -0.1;
02301
02302 glstate_->translate(dir[0], dir[1], dir[2]);
02303
02304 updateGL();
02305 lastMoveTime_.restart();
02306
02307 emit viewChanged();
02308 }
02309 }
02310
02311 void glViewer::moveBack() {
02312 if(navigationMode_ == FIRSTPERSON_NAVIGATION) {
02313 ACG::Vec3d dir = glstate_->viewing_direction();
02314
02315 dir *= 0.1;
02316
02317 glstate_->translate(dir[0], dir[1], dir[2]);
02318
02319 updateGL();
02320 lastMoveTime_.restart();
02321
02322 emit viewChanged();
02323 }
02324 }
02325
02326 void glViewer::strafeLeft() {
02327 if(navigationMode_ == FIRSTPERSON_NAVIGATION) {
02328 ACG::Vec3d dir = glstate_->right();
02329
02330 dir *= 0.1;
02331
02332 glstate_->translate(dir[0], dir[1], dir[2]);
02333
02334 updateGL();
02335 lastMoveTime_.restart();
02336
02337 emit viewChanged();
02338 }
02339 }
02340
02341 void glViewer::strafeRight() {
02342 if(navigationMode_ == FIRSTPERSON_NAVIGATION) {
02343 ACG::Vec3d dir = glstate_->right();
02344
02345 dir *= -0.1;
02346
02347 glstate_->translate(dir[0], dir[1], dir[2]);
02348
02349 updateGL();
02350 lastMoveTime_.restart();
02351
02352 emit viewChanged();
02353 }
02354 }
02355
02356
02357
02358