Developer Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
QtBaseViewerPicking.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 /*===========================================================================*\
43 * *
44 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 
50 
51 
52 
53 //=============================================================================
54 //
55 // CLASS glViewer - IMPLEMENTATION
56 //
57 //=============================================================================
58 
59 
60 //== INCLUDES =================================================================
61 
62 #include "QtBaseViewer.hh"
63 #include "QtGLGraphicsScene.hh"
64 #include "QtGLGraphicsView.hh"
65 
66 #if QT_VERSION < 0x050000
67 #include <QGLFramebufferObject>
68 #else
69 #include <QOpenGLFramebufferObject>
70 #endif
71 
72 //== NAMESPACES ===============================================================
73 
74 //== IMPLEMENTATION ==========================================================
75 
76 
78  const QPoint& _mousePos,
79  unsigned int& _nodeIdx,
80  unsigned int& _targetIdx,
81  ACG::Vec3d* _hitPointPtr )
82 {
83  if (sceneGraphRoot_)
84  {
85  // unsigned int node, target;
86  // QTime time;
87  // time.start ();
88  int rv = pickFromCache (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr);
89 
90  // cache will return -1 if a update is needed or caching is not supported
91  if (rv < 0)
92  rv = pickColor (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr);
93 
94  // printf ("ColorPicking took %d msec\n",time.restart ());
95 
96  // if (rv > 0 && (node != _nodeIdx || target != _targetIdx))
97  // printf ("***** Picking difference Color %d/%d GL %d/%d\n",node, target, _nodeIdx, _targetIdx);
98  if (rv > 0)
99  return rv;
100  }
101  return false;
102 }
103 
104 
105 //-----------------------------------------------------------------------------
106 
108  const QPoint& _mousePos,
109  unsigned int& _nodeIdx,
110  unsigned int& _targetIdx,
111  ACG::Vec3d* _hitPointPtr )
112 {
113  GLint w = glWidth(),
114  h = glHeight(),
115  l = scenePos().x(),
116  b = scene()->height () - scenePos().y() - h,
117  x = _mousePos.x(),
118  y = scene()->height () - _mousePos.y(),
119  pW = 1,
120  pH = 1;
121  GLubyte pixels[9][4];
122  GLfloat depths[9];
123  int hit = -1;
124 
125  // traversing order (center, top, bottom, ...)
126  unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 };
127 
129  {
130  // delete pick cache if the size changed
131  if (pickCache_ && pickCache_->size () != QSize (glWidth (), glHeight ()))
132  {
133  delete pickCache_;
134  pickCache_ = NULL;
135  }
136  // create a new pick cache frambuffer object
137  if (!pickCache_)
138  {
139  pickCache_ = new QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::Depth);
140  if (!pickCache_->isValid ())
141  {
142  pickCacheSupported_ = false;
143  delete pickCache_;
144  pickCache_ = NULL;
145  }
146  }
147  if (pickCache_)
148  {
149  // the viewport for the framebuffer object
150  l = 0;
151  b = 0;
152  x = _mousePos.x() - scenePos().x();
153  y = glHeight() - (_mousePos.y() - scenePos().y());
154 
155  // we can only pick inside of our window
156  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
157  return 0;
158 
159  pickCache_->bind ();
160  }
161  }
162 
163  const ACG::GLMatrixd& modelview = properties_.glState().modelview();
164  const ACG::GLMatrixd& projection = properties_.glState().projection();
165 
166  ACG::Vec4f clear_color = properties_.glState().clear_color();
167  properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
168 
169  // prepare GL state
170  makeCurrent();
171 
172  glViewport (l, b, w, h);
173  glMatrixMode(GL_PROJECTION);
174  glLoadIdentity();
175 
176  glMultMatrixd(projection.get_raw_data());
177  glMatrixMode(GL_MODELVIEW);
178  glLoadMatrixd(modelview.get_raw_data());
179  ACG::GLState::disable(GL_LIGHTING);
180  ACG::GLState::disable(GL_BLEND);
181  ACG::GLState::enable(GL_DEPTH_TEST);
182  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
183  properties_.glState().pick_init (true);
184 
185  // do the picking
187  ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState() );
188 
189  // restore GL state
190  glMatrixMode( GL_PROJECTION );
191  glLoadMatrixd(projection.get_raw_data());
192  glMatrixMode( GL_MODELVIEW );
193  glLoadMatrixd(modelview.get_raw_data());
194  ACG::GLState::enable(GL_LIGHTING);
195 
196  properties_.glState().set_clear_color (clear_color);
197 
198  if (properties_.glState().pick_error ())
199  {
200  if (pickCache_ && pickCache_->isBound ())
201  pickCache_->release ();
202 
203  std::cerr << "error - picking color stack invalid" << std::endl;
204  return -1;
205  }
206 
207  glPixelStorei(GL_PACK_ALIGNMENT, 1);
208  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
209 
210  // we can only read inside our viewport
211  if (x + 1 < w)
212  pW++;
213 
214  if (y + 1 < h)
215  pH++;
216 
217  if (x > 0)
218  {
219  x--;
220  pW++;
221  }
222  if (y > 0)
223  {
224  y--;
225  pH++;
226  }
227 
228  if (pH != 3 || pW != 3)
229  {
230  // initialize unused values with 0
231  for (int i = 0; i < 9; i++)
232  {
233  pixels[i][0] = 0;
234  pixels[i][1] = 0;
235  pixels[i][2] = 0;
236  pixels[i][3] = 0;
237  depths[i] = 0.0;
238  }
239  }
240 
241  // read from framebuffer
242  glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
243  glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths);
244 
245  // unbind pick cache
246  if (pickCache_ && pickCache_->isBound ())
247  {
248  pickCache_->release ();
249  updatePickCache_ = false;
250  pickCacheTarget_ = _pickTarget;
251  }
252 
253  // get first found pixel
254  for (int i = 0; i < 9; i++)
255  {
256  if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0))
257  {
258  hit = order[i];
259  break;
260  }
261  }
262 
263  if (hit < 0)
264  return 0;
265 
266 
267  ACG::Vec4uc rgba;
268  rgba[0] = pixels[hit][0];
269  rgba[1] = pixels[hit][1];
270  rgba[2] = pixels[hit][2];
271  rgba[3] = pixels[hit][3];
272 
273  std::vector<unsigned int> rv = properties_.glState().pick_color_to_stack (rgba);
274 
275  // something wrong with the color stack ?
276  if (rv.size () < 2)
277  {
278  std::cerr << "error - picking color not found in stack" << std::endl;
279  return -1;
280  }
281 
282  _nodeIdx = rv[1];
283  _targetIdx = rv[0];
284 
285 // // Debug Code to visualize picking cache ( DO NOT REMOVE!!!! Jan )
286 // QImage murks(glWidth (),glHeight (),QImage::Format_ARGB32);
287 // murks = pickCache_->toImage();
288 // for ( int i = 0 ; i < glWidth() ; ++i )
289 // for ( int j = 0 ; j < glHeight() ; ++j ) {
290 // QColor bla (murks.pixel(i,j));
291 // bla.setAlpha(255);
292 // murks.setPixel(i,j,bla.rgba());
293 // }
294 // murks.save("murks.png");
295 
296  if (_hitPointPtr)
297  {
298  *_hitPointPtr = properties_.glState().unproject (
299  ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit]));
300  }
301 
302  return 1;
303 }
304 
305 //-----------------------------------------------------------------------------
306 
308  const QPoint& _mousePos,
309  unsigned int& _nodeIdx,
310  unsigned int& _targetIdx,
311  ACG::Vec3d* _hitPointPtr )
312 {
313  // do we need an update?
315  pickCacheTarget_ != _pickTarget)
316  return -1;
317 
318  GLint x = _mousePos.x() - scenePos().x(),
319  y = glHeight() - (_mousePos.y() - scenePos().y()),
320  pW = 1,
321  pH = 1;
322  GLubyte pixels[9][4];
323  GLfloat depths[9];
324  int hit = -1;
325 
326  // traversing order (center, top, bottom, ...)
327  unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 };
328 
329  // can't pick outside
330  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
331  return 0;
332 
333  // bind cache framebuffer object
334  pickCache_->bind ();
335 
336  glPixelStorei(GL_PACK_ALIGNMENT, 1);
337  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
338 
339  // we can only read inside our viewport
340  if (x + 1 < (int)glWidth ())
341  pW++;
342 
343  if (y + 1 < (int)glHeight ())
344  pH++;
345 
346  if (x > 0)
347  {
348  x--;
349  pW++;
350  }
351  if (y > 0)
352  {
353  y--;
354  pH++;
355  }
356 
357  if (pH != 3 || pW != 3)
358  {
359  // initialize unused values with 0
360  for (int i = 0; i < 9; i++)
361  {
362  pixels[i][0] = 0;
363  pixels[i][1] = 0;
364  pixels[i][2] = 0;
365  pixels[i][3] = 0;
366  depths[i] = 0.0;
367  }
368  }
369 
370  // read from framebuffer
371  glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
372  glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths);
373 
374  // unbind
375  pickCache_->release ();
376 
377  // get first found pixel
378  for (int i = 0; i < 9; i++)
379  {
380  if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0))
381  {
382  hit = order[i];
383  break;
384  }
385  }
386 
387  if (hit < 0)
388  return 0;
389 
390 
391  ACG::Vec4uc rgba;
392  rgba[0] = pixels[hit][0];
393  rgba[1] = pixels[hit][1];
394  rgba[2] = pixels[hit][2];
395  rgba[3] = pixels[hit][3];
396 
397  std::vector<unsigned int> rv = properties_.glState().pick_color_to_stack (rgba);
398 
399  // something wrong with the color stack ?
400  if (rv.size () < 2)
401  return -1;
402 
403  _nodeIdx = rv[1];
404  _targetIdx = rv[0];
405 
406  if (_hitPointPtr)
407  {
408  *_hitPointPtr = properties_.glState().unproject(
409  ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit]));
410  }
411 
412  return 1;
413 }
414 
415 //-----------------------------------------------------------------------------
416 
418  const QRegion& _region,
419  QList<QPair<unsigned int, unsigned int> >& _list,
420  QVector<float>* _depths,
421  QVector<ACG::Vec3d>* _points)
422 {
423  QRect rect = _region.boundingRect();
424  GLint w = glWidth(),
425  h = glHeight(),
426  l = scenePos().x(),
427  b = scene()->height () - scenePos().y() - h,
428  x = rect.x(),
429  y = scene()->height () - rect.bottom();
430 
431  GLubyte* buffer = 0;
432  GLfloat* depths = 0;
433 
435  {
436  // delete pick cache if the size changed
437  if (pickCache_ && pickCache_->size () != QSize (glWidth (), glHeight ()))
438  {
439  delete pickCache_;
440  pickCache_ = NULL;
441  }
442  // create a new pick cache frambuffer object
443  if (!pickCache_)
444  {
445  pickCache_ = new QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::Depth);
446  if (!pickCache_->isValid ())
447  {
448  pickCacheSupported_ = false;
449  delete pickCache_;
450  pickCache_ = NULL;
451  }
452  }
453  if (pickCache_)
454  {
455  // the viewport for the framebuffer object
456  l = 0;
457  b = 0;
458  x = rect.x() - scenePos().x();
459  y = glHeight() - (rect.bottom() - scenePos().y());
460 
461  // we can only pick inside of our window
462  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
463  return 0;
464 
465  pickCache_->bind ();
466  }
467  }
468 
469  const ACG::GLMatrixd& modelview = properties_.glState().modelview();
470  const ACG::GLMatrixd& projection = properties_.glState().projection();
471 
472  ACG::Vec4f clear_color = properties_.glState().clear_color();
473  properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
474 
475  // prepare GL state
476  makeCurrent();
477 
478  glViewport (l, b, w, h);
479  glMatrixMode(GL_PROJECTION);
480  glLoadIdentity();
481 
482  glMultMatrixd(projection.get_raw_data());
483  glMatrixMode(GL_MODELVIEW);
484  glLoadMatrixd(modelview.get_raw_data());
485  ACG::GLState::disable(GL_LIGHTING);
486  ACG::GLState::disable(GL_BLEND);
487  ACG::GLState::enable(GL_DEPTH_TEST);
488  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
489  properties_.glState().pick_init (true);
490 
491  // do the picking
493  ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState());
494 
495  // restore GL state
496  glMatrixMode( GL_PROJECTION );
497  glLoadMatrixd(projection.get_raw_data());
498  glMatrixMode( GL_MODELVIEW );
499  glLoadMatrixd(modelview.get_raw_data());
500  ACG::GLState::enable(GL_LIGHTING);
501  ACG::GLState::enable(GL_BLEND);
502 
503  properties_.glState().set_clear_color(clear_color);
504 
505  if (properties_.glState().pick_error ())
506  {
507  if (pickCache_ && pickCache_->isBound ())
508  pickCache_->release ();
509  return false;
510  }
511 
512  buffer = new GLubyte[4 * rect.width() * rect.height()];
513 
514 
515 
516  glPixelStorei(GL_PACK_ALIGNMENT, 1);
517  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
518 
519  glReadPixels (x, y, rect.width(),
520  rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, buffer);
521 
522  if (_depths || _points ) {
523  depths = new GLfloat[ rect.width() * rect.height() ];
524  glReadPixels (x, y, rect.width(), rect.height(), GL_DEPTH_COMPONENT, GL_FLOAT, depths);
525 
526  /* Debug code, writing out the depth image
527  QImage depthmapimage(rect.width(), rect.height(), QImage::Format_Indexed8);
528 
529  // color map
530  for ( int i = 0 ; i <= 255 ; i++ )
531  depthmapimage.setColor( i, qRgb( i, i, i ) );
532 
533  for ( int i = 0 ; i < rect.width() ; i++ )
534  for ( int j = 0 ; j < rect.height() ; j++ )
535  {
536  depthmapimage.setPixel(i,rect.height()-j-1, (unsigned int)(depths[j*rect.width()+i]*255));
537  }
538 
539  depthmapimage.save("test.png");
540  */
541  }
542 
543  // Iterate over the bounding rectangle of the region
544  for (int y = 0; y < rect.height (); y++)
545  for (int x = 0; x < rect.width (); x++)
546  {
547 
548  // Check if the current point is in the polygon of the region
549  if (_region.contains (QPoint (rect.x() + x, rect.y() + y)))
550  {
551 
552  // Calculate position inside the buffer
553  const int bPos = (((rect.height () - (y + 1)) * rect.width ()) + x) * 4;
554 
555  // Get the picking color from the buffer at the current position
556  if (buffer[bPos + 2] != 0 || buffer[bPos + 1] != 0 || buffer[bPos] != 0 || buffer[bPos + 3] != 0)
557  {
558  ACG::Vec4uc rgba;
559  rgba[0] = buffer[bPos];
560  rgba[1] = buffer[bPos + 1];
561  rgba[2] = buffer[bPos + 2];
562  rgba[3] = buffer[bPos + 3];
563 
564  std::vector<unsigned int> rv = properties_.glState().pick_color_to_stack (rgba);
565  if (rv.size () < 2)
566  continue;
567 
568  QPair<unsigned int, unsigned int> curr(rv[1], rv[0]);
569 
570  // added a new (targetidx/nodeidx) pair
571  if( !_list.contains(curr))
572  {
573  _list << curr;
574 
575  if ( _depths || _points ) {
576 
577  const double curr_depth(depths[(rect.height()-(y+1))*rect.width() + x]);
578 
579  // If depths should be returned, we extract it here
580  if (_depths)
581  (*_depths) << curr_depth;
582 
583  // unproject depth to real (3D) depth value
584  if ( _points )
585  (*_points) << properties_.glState().unproject(ACG::Vec3d(x+rect.x(),h-(y+rect.y()),curr_depth));
586  }
587  }
588  }
589  }
590  }
591 
592  delete[] buffer;
593 
594  if ( _depths || _points )
595  delete[] depths;
596 
597  // unbind pick cache
598  if (pickCache_ && pickCache_->isBound ())
599  {
600  pickCache_->release ();
601  updatePickCache_ = false;
602  pickCacheTarget_ = _pickTarget;
603  }
604 
605  return true;
606 }
607 
608 
609 //-----------------------------------------------------------------------------
610 
611 bool
613 fast_pick( const QPoint& _mousePos,
614  ACG::Vec3d& _hitPoint )
615 {
616  // get x,y,z values of pixel
617  GLint x(_mousePos.x()), y(glHeight() - _mousePos.y());
618  GLfloat z;
619 
620 
621  makeCurrent();
622  glPixelStorei(GL_PACK_ALIGNMENT, 1);
623  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
624  glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
625 
626 
627  if (z < 0.99999)
628  {
629  _hitPoint = properties_.glState().unproject( ACG::Vec3d(x, y, z) );
630  return true;
631  }
632  else return false;
633 }
634 
635 
636 //=============================================================================
637 
638 //=============================================================================
static void enable(GLenum _cap)
replaces glEnable, but supports locking
Definition: GLState.cc:1490
bool pick_region(ACG::SceneGraph::PickTarget _pickTarget, const QRegion &_region, QList< QPair< unsigned int, unsigned int > > &_list, QVector< float > *_depths=0, QVector< ACG::Vec3d > *_points=0)
Perform picking action n a whole region.
PickTarget
What target to use for picking.
Definition: BaseNode.hh:99
const Vec4f & clear_color() const
get background color
Definition: GLState.hh:924
Vec3d unproject(const Vec3d &_winPoint) const
unproject point in window coordinates _winPoint to world coordinates
Definition: GLState.cc:649
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
const GLMatrixd & modelview() const
get modelview matrix
Definition: GLState.hh:794
void traverse_multipass(BaseNode *_node, Action &_action, const unsigned int &_pass)
Definition: SceneGraph.hh:260
const GLMatrixd & projection() const
get projection matrix
Definition: GLState.hh:789
unsigned int glWidth() const
get width of QGLWidget
ACG::SceneGraph::PickTarget pickCacheTarget_
Pick target stored in pick cache.
bool pick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, unsigned int &_nodeIdx, unsigned int &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
QGLFramebufferObject QFramebufferObject
Framebuffer object that holds the pick cache.
virtual void makeCurrent()
Makes this widget the current widget for OpenGL operations.
bool fast_pick(const QPoint &_mousePos, ACG::Vec3d &_hitPoint)
void pick_init(bool _color)
initialize name/color picking stack (like glInitNames())
Definition: GLState.cc:1039
bool pickCacheSupported_
Is pick caching supported.
QFramebufferObject * pickCache_
Framebuffer object that holds the pick cache.
static void disable(GLenum _cap)
replaces glDisable, but supports locking
Definition: GLState.cc:1504
std::vector< unsigned int > pick_color_to_stack(Vec4uc _rgba) const
Definition: GLState.cc:1087
bool updatePickCache_
Should the pick cache be updated.
bool pick_error() const
Definition: GLState.cc:1105
unsigned int glHeight() const
get height of QGLWidget
Viewer::ViewerProperties & properties_
All properties for this viewer.
ACG::GLState & glState()
Get the glState of the Viewer.
int pickColor(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, unsigned int &_nodeIdx, unsigned int &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
pick using colors
const Scalar * get_raw_data() const
Definition: Matrix4x4T.hh:262
void set_clear_color(const Vec4f &_col)
set background color
Definition: GLState.cc:660
int pickFromCache(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, unsigned int &_nodeIdx, unsigned int &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
pick from cache