/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see . *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision$ *
* $LastChangedBy$ *
* $Date$ *
* *
\*===========================================================================*/
//=============================================================================
//
// CLASS glViewer - IMPLEMENTATION
//
//=============================================================================
//== INCLUDES =================================================================
#include "QtBaseViewer.hh"
#include "QtGLGraphicsScene.hh"
#include "QtGLGraphicsView.hh"
#if QT_VERSION < 0x050000
#include
#else
#include
#endif
//== NAMESPACES ===============================================================
//== IMPLEMENTATION ==========================================================
bool glViewer::pick( ACG::SceneGraph::PickTarget _pickTarget,
const QPoint& _mousePos,
unsigned int& _nodeIdx,
unsigned int& _targetIdx,
ACG::Vec3d* _hitPointPtr )
{
if (sceneGraphRoot_)
{
// unsigned int node, target;
// QTime time;
// time.start ();
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;
// node = _nodeIdx;
// target = _targetIdx;
if (rv < 0)
rv = pickGL (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr);
// printf ("GLPicking took %d msec\n",time.restart ());
// if (rv > 0 && (node != _nodeIdx || target != _targetIdx))
// printf ("***** Picking difference Color %d/%d GL %d/%d\n",node, target, _nodeIdx, _targetIdx);
if (rv > 0)
return rv;
}
return false;
}
//-----------------------------------------------------------------------------
int glViewer::pickColor( ACG::SceneGraph::PickTarget _pickTarget,
const QPoint& _mousePos,
unsigned int& _nodeIdx,
unsigned int& _targetIdx,
ACG::Vec3d* _hitPointPtr )
{
GLint w = glWidth(),
h = glHeight(),
l = scenePos().x(),
b = scene()->height () - scenePos().y() - h,
x = _mousePos.x(),
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 QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::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();
ACG::Vec4f clear_color = properties_.glState().clear_color();
properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
// prepare GL state
makeCurrent();
glViewport (l, b, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixd(projection.get_raw_data());
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(modelview.get_raw_data());
ACG::GLState::disable(GL_LIGHTING);
ACG::GLState::disable(GL_BLEND);
ACG::GLState::enable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
properties_.glState().pick_init (true);
// do the picking
ACG::SceneGraph::PickAction action(properties_.glState(), _pickTarget, properties_.drawMode());
ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState() );
// restore GL state
glMatrixMode( GL_PROJECTION );
glLoadMatrixd(projection.get_raw_data());
glMatrixMode( GL_MODELVIEW );
glLoadMatrixd(modelview.get_raw_data());
ACG::GLState::enable(GL_LIGHTING);
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);
// 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;
pickCacheTarget_ = _pickTarget;
}
// 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];
// // Debug Code to visualize picking cache ( DO NOT REMOVE!!!! Jan )
// QImage murks(glWidth (),glHeight (),QImage::Format_ARGB32);
// murks = pickCache_->toImage();
// for ( int i = 0 ; i < glWidth() ; ++i )
// for ( int j = 0 ; j < glHeight() ; ++j ) {
// QColor bla (murks.pixel(i,j));
// bla.setAlpha(255);
// murks.setPixel(i,j,bla.rgba());
// }
// murks.save("murks.png");
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_ ||
pickCacheTarget_ != _pickTarget)
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))
{
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;
}
//-----------------------------------------------------------------------------
bool glViewer::pickGL( ACG::SceneGraph::PickTarget _pickTarget,
const QPoint& _mousePos,
unsigned int& _nodeIdx,
unsigned int& _targetIdx,
ACG::Vec3d* _hitPointPtr )
{
///@TODO: Remove that function alltogether.
std::cerr << "Error! OpenGL Picking via OpenGL Name stack is not supported anymore." << std::endl;
return false;
}
//-----------------------------------------------------------------------------
bool glViewer::pick_region( ACG::SceneGraph::PickTarget _pickTarget,
const QRegion& _region,
QList >& _list,
QVector* _depths,
QVector* _points)
{
QRect rect = _region.boundingRect();
GLint w = glWidth(),
h = glHeight(),
l = scenePos().x(),
b = scene()->height () - scenePos().y() - h,
x = rect.x(),
y = scene()->height () - rect.bottom();
GLubyte* buffer = 0;
GLfloat* depths = 0;
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 QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::Depth);
if (!pickCache_->isValid ())
{
pickCacheSupported_ = false;
delete pickCache_;
pickCache_ = NULL;
}
}
if (pickCache_)
{
// the viewport for the framebuffer object
l = 0;
b = 0;
x = rect.x() - scenePos().x();
y = glHeight() - (rect.bottom() - 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();
ACG::Vec4f clear_color = properties_.glState().clear_color();
properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
// prepare GL state
makeCurrent();
glViewport (l, b, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMultMatrixd(projection.get_raw_data());
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(modelview.get_raw_data());
ACG::GLState::disable(GL_LIGHTING);
ACG::GLState::disable(GL_BLEND);
ACG::GLState::enable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
properties_.glState().pick_init (true);
// do the picking
ACG::SceneGraph::PickAction action(properties_.glState(), _pickTarget, properties_.drawMode());
ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState());
// restore GL state
glMatrixMode( GL_PROJECTION );
glLoadMatrixd(projection.get_raw_data());
glMatrixMode( GL_MODELVIEW );
glLoadMatrixd(modelview.get_raw_data());
ACG::GLState::enable(GL_LIGHTING);
ACG::GLState::enable(GL_BLEND);
properties_.glState().set_clear_color(clear_color);
if (properties_.glState().pick_error ())
{
if (pickCache_ && pickCache_->isBound ())
pickCache_->release ();
return false;
}
buffer = new GLubyte[4 * rect.width() * rect.height()];
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels (x, y, rect.width(),
rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, buffer);
if (_depths || _points ) {
depths = new GLfloat[ rect.width() * rect.height() ];
glReadPixels (x, y, rect.width(), rect.height(), GL_DEPTH_COMPONENT, GL_FLOAT, depths);
/* Debug code, writing out the depth image
QImage depthmapimage(rect.width(), rect.height(), QImage::Format_Indexed8);
// color map
for ( int i = 0 ; i <= 255 ; i++ )
depthmapimage.setColor( i, qRgb( i, i, i ) );
for ( int i = 0 ; i < rect.width() ; i++ )
for ( int j = 0 ; j < rect.height() ; j++ )
{
depthmapimage.setPixel(i,rect.height()-j-1, (unsigned int)(depths[j*rect.width()+i]*255));
}
depthmapimage.save("test.png");
*/
}
// Iterate over the bounding rectangle of the region
for (int y = 0; y < rect.height (); y++)
for (int x = 0; x < rect.width (); x++)
{
// Check if the current point is in the polygon of the region
if (_region.contains (QPoint (rect.x() + x, rect.y() + y)))
{
// Calculate position inside the buffer
const int bPos = (((rect.height () - (y + 1)) * rect.width ()) + x) * 4;
// Get the picking color from the buffer at the current position
if (buffer[bPos + 2] != 0 || buffer[bPos + 1] != 0 || buffer[bPos] != 0 || buffer[bPos + 3] != 0)
{
ACG::Vec4uc rgba;
rgba[0] = buffer[bPos];
rgba[1] = buffer[bPos + 1];
rgba[2] = buffer[bPos + 2];
rgba[3] = buffer[bPos + 3];
std::vector rv = properties_.glState().pick_color_to_stack (rgba);
if (rv.size () < 2)
continue;
QPair curr(rv[1], rv[0]);
// added a new (targetidx/nodeidx) pair
if( !_list.contains(curr))
{
_list << curr;
if ( _depths || _points ) {
const double curr_depth(depths[(rect.height()-(y+1))*rect.width() + x]);
// If depths should be returned, we extract it here
if (_depths)
(*_depths) << curr_depth;
// unproject depth to real (3D) depth value
if ( _points )
(*_points) << properties_.glState().unproject(ACG::Vec3d(x+rect.x(),h-(y+rect.y()),curr_depth));
}
}
}
}
}
delete[] buffer;
if ( _depths || _points )
delete[] depths;
// unbind pick cache
if (pickCache_ && pickCache_->isBound ())
{
pickCache_->release ();
updatePickCache_ = false;
pickCacheTarget_ = _pickTarget;
}
return true;
}
//-----------------------------------------------------------------------------
bool
glViewer::
fast_pick( const QPoint& _mousePos,
ACG::Vec3d& _hitPoint )
{
// get x,y,z values of pixel
GLint x(_mousePos.x()), y(glHeight() - _mousePos.y());
GLfloat z;
makeCurrent();
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
if (z < 0.99999)
{
_hitPoint = properties_.glState().unproject( ACG::Vec3d(x, y, z) );
return true;
}
else return false;
}
//=============================================================================
//=============================================================================