From 09259380aa35b9d1085e1511db7ddd4d44fef8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Fri, 7 Aug 2009 06:00:29 +0000 Subject: [PATCH] Added picking cache. Set always a perspective projection matrix Stereo mode. Setting a orthogonal matrix in the state and rendering with the perspective stereo matrix breaks the project/unprojoce calls. git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@6802 383ad7c9-94d9-4d36-a494-682f7c89f535 --- widgets/glWidget/QtBaseViewer.cc | 69 ++++---- widgets/glWidget/QtBaseViewer.hh | 19 +++ widgets/glWidget/QtBaseViewerPicking.cc | 209 +++++++++++++++++++++++- widgets/glWidget/QtBaseViewerStereo.cc | 2 + 4 files changed, 255 insertions(+), 44 deletions(-) diff --git a/widgets/glWidget/QtBaseViewer.cc b/widgets/glWidget/QtBaseViewer.cc index 8d28f4a7..ae832446 100644 --- a/widgets/glWidget/QtBaseViewer.cc +++ b/widgets/glWidget/QtBaseViewer.cc @@ -142,6 +142,9 @@ glViewer::glViewer( QGraphicsScene* _scene, glWidget_(_glWidget), cursorPainter_(0), cursorPositionValid_(false), + pickCache_(0), + updatePickCache_(true), + pickCacheSupported_(true), clickEvent_(QEvent::MouseButtonPress, QPoint (), Qt::NoButton, Qt::NoButton, Qt::NoModifier), properties_(_properties), glstate_(0), @@ -339,30 +342,24 @@ void glViewer::updateProjectionMatrix() glstate_->reset_projection(); - switch (projectionMode_) + // In scereo mode we have to use a perspective matrix + if (stereo_ || projectionMode_ == PERSPECTIVE_PROJECTION) { - case ORTHOGRAPHIC_PROJECTION: - { - double aspect; - - if (isVisible()) - aspect = (double) glWidth() / (double) glHeight(); - else - aspect = 1.0; + glstate_->perspective(fovy_, (GLdouble) glWidth() / (GLdouble) glHeight(), + near_, far_); + } + else + { + double aspect; - glstate_->ortho( -orthoWidth_, orthoWidth_, - -orthoWidth_/aspect, orthoWidth_/aspect, - near_, far_ ); - break; - } + if (isVisible()) + aspect = (double) glWidth() / (double) glHeight(); + else + aspect = 1.0; - case PERSPECTIVE_PROJECTION: - { - glstate_->perspective(fovy_, - (GLdouble) glWidth() / (GLdouble) glHeight(), - near_, far_); - break; - } + glstate_->ortho( -orthoWidth_, orthoWidth_, + -orthoWidth_/aspect, orthoWidth_/aspect, + near_, far_ ); } } @@ -452,6 +449,7 @@ void glViewer::updateGL() { if (!properties_.updateLocked() && isVisible() ) { + updatePickCache_ = true; update(); emit viewUpdated(); @@ -1905,26 +1903,19 @@ void glViewer::viewWheelEvent( QWheelEvent* _event) if (_event->modifiers() == Qt::ShiftModifier) factor = properties_.wheelZoomFactorShift(); - switch (projectionMode()) + if (projectionMode() == PERSPECTIVE_PROJECTION || stereo_) { - case PERSPECTIVE_PROJECTION: - { - double d = -(double)_event->delta() / 120.0 * 0.2 * factor * trackball_radius_/3; - translate( ACG::Vec3d(0.0, 0.0, d) ); - updateGL(); - break; - } - - case ORTHOGRAPHIC_PROJECTION: - { - double d = (double)_event->delta() / 120.0 * 0.2 * factor * orthoWidth_; - orthoWidth_ += d; - updateProjectionMatrix(); - updateGL(); - break; - } + double d = -(double)_event->delta() / 120.0 * 0.2 * factor * trackball_radius_/3; + translate( ACG::Vec3d(0.0, 0.0, d) ); + updateGL(); + } + else + { + double d = (double)_event->delta() / 120.0 * 0.2 * factor * orthoWidth_; + orthoWidth_ += d; + updateProjectionMatrix(); + updateGL(); } - } diff --git a/widgets/glWidget/QtBaseViewer.hh b/widgets/glWidget/QtBaseViewer.hh index 93969b4f..ca1f2918 100644 --- a/widgets/glWidget/QtBaseViewer.hh +++ b/widgets/glWidget/QtBaseViewer.hh @@ -94,6 +94,7 @@ class QSplitter; class QTimer; class QImage; class QSocketNotifier; +class QGLFramebufferObject; //== NAMESPACES =============================================================== @@ -785,6 +786,24 @@ private: unsigned int& _targetIdx, ACG::Vec3d* _hitPointPtr=0 ); + /// pick from cache + int pickFromCache( ACG::SceneGraph::PickTarget _pickTarget, + const QPoint& _mousePos, + unsigned int& _nodeIdx, + unsigned int& _targetIdx, + ACG::Vec3d* _hitPointPtr=0 ); + + private: + + /// Framebuffer object that holds the pick cache + QGLFramebufferObject *pickCache_; + + /// Should the pick cache be updated + bool updatePickCache_; + + /// Is pick caching supported + bool pickCacheSupported_; + /** @} */ //=========================================================================== diff --git a/widgets/glWidget/QtBaseViewerPicking.cc b/widgets/glWidget/QtBaseViewerPicking.cc index c20b49ed..b51f5ae9 100644 --- a/widgets/glWidget/QtBaseViewerPicking.cc +++ b/widgets/glWidget/QtBaseViewerPicking.cc @@ -56,6 +56,8 @@ #include "QtGLGraphicsScene.hh" #include "QtGLGraphicsView.hh" +#include + //== NAMESPACES =============================================================== //== IMPLEMENTATION ========================================================== @@ -77,7 +79,11 @@ bool glViewer::pick( ACG::SceneGraph::PickTarget _pickTarget, // unsigned int node, target; // QTime time; // time.start (); - int rv = pickColor (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr); + int rv = pickFromCache (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr); + + // cache will return -1 if a update is needed or caching is not supported + if (rv < 0) + rv = pickColor (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr); // printf ("ColorPicking took %d msec\n",time.restart ()); // rv = -1; @@ -109,12 +115,51 @@ int glViewer::pickColor( ACG::SceneGraph::PickTarget _pickTarget, l = scenePos().x(), b = scene()->height () - scenePos().y() - h, x = _mousePos.x(), - y = scene()->height () - _mousePos.y(); + y = scene()->height () - _mousePos.y(), + pW = 1, + pH = 1; GLubyte pixels[9][4]; GLfloat depths[9]; int hit = -1; + + // traversing order (center, top, bottom, ...) unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 }; + if (pickCacheSupported_) + { + // delete pick cache if the size changed + if (pickCache_ && pickCache_->size () != QSize (glWidth (), glHeight ())) + { + delete pickCache_; + pickCache_ = NULL; + } + // create a new pick cache frambuffer object + if (!pickCache_) + { + pickCache_ = new QGLFramebufferObject (glWidth (), glHeight (), QGLFramebufferObject::Depth); + if (!pickCache_->isValid ()) + { + pickCacheSupported_ = false; + delete pickCache_; + pickCache_ = NULL; + } + } + if (pickCache_) + { + // the viewport for the framebuffer object + l = 0; + b = 0; + x = _mousePos.x() - scenePos().x(); + y = glHeight() - (_mousePos.y() - scenePos().y()); + + // we can only pick inside of our window + if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight()) + return 0; + + pickCache_->bind (); + } + } + const ACG::GLMatrixd& modelview = properties_.glState().modelview(); const ACG::GLMatrixd& projection = properties_.glState().projection(); @@ -151,14 +196,167 @@ int glViewer::pickColor( ACG::SceneGraph::PickTarget _pickTarget, properties_.glState().set_clear_color (clear_color); if (properties_.glState().pick_error ()) + { + if (pickCache_ && pickCache_->isBound ()) + pickCache_->release (); return -1; + } glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glReadPixels (x - 1, y - 1, 3, 3, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - glReadPixels (x - 1, y - 1, 3, 3, GL_DEPTH_COMPONENT, GL_FLOAT, depths); + // we can only read inside our viewport + if (x + 1 < w) + pW++; + + if (y + 1 < h) + pH++; + + if (x > 0) + { + x--; + pW++; + } + if (y > 0) + { + y--; + pH++; + } + + if (pH != 3 || pW != 3) + { + // initialize unused values with 0 + for (int i = 0; i < 9; i++) + { + pixels[i][0] = 0; + pixels[i][1] = 0; + pixels[i][2] = 0; + pixels[i][3] = 0; + depths[i] = 0.0; + } + } + + // read from framebuffer + glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths); + + // unbind pick cache + if (pickCache_ && pickCache_->isBound ()) + { + pickCache_->release (); + updatePickCache_ = false; + } + + // get first found pixel + for (int i = 0; i < 9; i++) + { + if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0)) + { + hit = order[i]; + break; + } + } + + if (hit < 0) + return 0; + + + ACG::Vec4uc rgba; + rgba[0] = pixels[hit][0]; + rgba[1] = pixels[hit][1]; + rgba[2] = pixels[hit][2]; + rgba[3] = pixels[hit][3]; + + std::vector rv = properties_.glState().pick_color_to_stack (rgba); + + // something wrong with the color stack ? + if (rv.size () < 2) + return -1; + + _nodeIdx = rv[1]; + _targetIdx = rv[0]; + + if (_hitPointPtr) + { + *_hitPointPtr = properties_.glState().unproject ( + ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit])); + } + + return 1; +} + +//----------------------------------------------------------------------------- + +int glViewer::pickFromCache( ACG::SceneGraph::PickTarget _pickTarget, + const QPoint& _mousePos, + unsigned int& _nodeIdx, + unsigned int& _targetIdx, + ACG::Vec3d* _hitPointPtr ) +{ + // do we need an update? + if (!pickCacheSupported_ || updatePickCache_ || !pickCache_) + return -1; + + GLint x = _mousePos.x() - scenePos().x(), + y = glHeight() - (_mousePos.y() - scenePos().y()), + pW = 1, + pH = 1; + GLubyte pixels[9][4]; + GLfloat depths[9]; + int hit = -1; + + // traversing order (center, top, bottom, ...) + unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 }; + + // can't pick outside + if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight()) + return 0; + + // bind cache framebuffer object + pickCache_->bind (); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // we can only read inside our viewport + if (x + 1 < (int)glWidth ()) + pW++; + + if (y + 1 < (int)glHeight ()) + pH++; + + if (x > 0) + { + x--; + pW++; + } + if (y > 0) + { + y--; + pH++; + } + + if (pH != 3 || pW != 3) + { + // initialize unused values with 0 + for (int i = 0; i < 9; i++) + { + pixels[i][0] = 0; + pixels[i][1] = 0; + pixels[i][2] = 0; + pixels[i][3] = 0; + depths[i] = 0.0; + } + } + + // read from framebuffer + glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths); + + // unbind + pickCache_->release (); + // get first found pixel for (int i = 0; i < 9; i++) { if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0)) @@ -189,7 +387,8 @@ int glViewer::pickColor( ACG::SceneGraph::PickTarget _pickTarget, if (_hitPointPtr) { - *_hitPointPtr = properties_.glState().unproject(ACG::Vec3d(x,y,depths[hit])); + *_hitPointPtr = properties_.glState().unproject( + ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit])); } return 1; diff --git a/widgets/glWidget/QtBaseViewerStereo.cc b/widgets/glWidget/QtBaseViewerStereo.cc index f7b17d16..1d54df87 100644 --- a/widgets/glWidget/QtBaseViewerStereo.cc +++ b/widgets/glWidget/QtBaseViewerStereo.cc @@ -92,6 +92,8 @@ glViewer::setStereoMode(bool _b) glDrawBuffer(GL_BACK); } + updateProjectionMatrix (); + updateGL(); } -- GitLab