Developer Documentation
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 
45 
46 
47 //=============================================================================
48 //
49 // CLASS glViewer - IMPLEMENTATION
50 //
51 //=============================================================================
52 
53 
54 //== INCLUDES =================================================================
55 
56 #include "QtBaseViewer.hh"
57 #include "QtGLGraphicsScene.hh"
58 #include "QtGLGraphicsView.hh"
59 
61 
62 
63 #include <QOpenGLFramebufferObject>
64 
65 
66 //== NAMESPACES ===============================================================
67 
68 //== IMPLEMENTATION ==========================================================
69 
70 
72  const QPoint& _mousePos,
73  size_t& _nodeIdx,
74  size_t& _targetIdx,
75  ACG::Vec3d* _hitPointPtr )
76 {
77  if (sceneGraphRoot_)
78  {
79  // unsigned int node, target;
80  // QTime time;
81  // time.start ();
82  int rv = pickFromCache (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr);
83 
84  // cache will return -1 if a update is needed or caching is not supported
85  if (rv < 0)
86  rv = pickColor (_pickTarget, _mousePos, _nodeIdx, _targetIdx, _hitPointPtr);
87 
88  // printf ("ColorPicking took %d msec\n",time.restart ());
89 
90  // if (rv > 0 && (node != _nodeIdx || target != _targetIdx))
91  // printf ("***** Picking difference Color %d/%d GL %d/%d\n",node, target, _nodeIdx, _targetIdx);
92  if (rv > 0)
93  return rv;
94  }
95  return false;
96 }
97 
98 
99 //-----------------------------------------------------------------------------
100 
102  const QPoint& _mousePos,
103  size_t& _nodeIdx,
104  size_t& _targetIdx,
105  ACG::Vec3d* _hitPointPtr )
106 {
107  GLint w = glWidth(),
108  h = glHeight(),
109  l = scenePos().x(),
110  b = scene()->height () - scenePos().y() - h,
111  x = _mousePos.x(),
112  y = scene()->height () - _mousePos.y(),
113  pW = 1,
114  pH = 1;
115  GLubyte pixels[9][4];
116  GLfloat depths[9];
117  int hit = -1;
118 
119  // traversing order (center, top, bottom, ...)
120  unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 };
121 
122  // prepare GL state
123  makeCurrent();
124  if(OpenFlipper::Options::coreProfile())
125  defaultVAO_.bind();
126 
128  {
129  // delete pick cache if the size changed
130  if (pickCache_ && pickCache_->size () != QSize (glWidth (), glHeight ()))
131  {
132  delete pickCache_;
133  pickCache_ = NULL;
134  }
135  // create a new pick cache frambuffer object
136  if (!pickCache_)
137  {
138  pickCache_ = new QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::Depth);
139  if (!pickCache_->isValid ())
140  {
141  pickCacheSupported_ = false;
142  delete pickCache_;
143  pickCache_ = NULL;
144  }
145  }
146  if (pickCache_)
147  {
148  // the viewport for the framebuffer object
149  l = 0;
150  b = 0;
151  x = _mousePos.x() - scenePos().x();
152  y = glHeight() - (_mousePos.y() - scenePos().y());
153 
154  // we can only pick inside of our window
155  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
156  return 0;
157 
158  pickCache_->bind ();
159  }
160  }
161 
162  const ACG::GLMatrixd& modelview = properties_.glState().modelview();
163  const ACG::GLMatrixd& projection = properties_.glState().projection();
164 
165  ACG::Vec4f clear_color = properties_.glState().clear_color();
166  properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
167 
168 
169  glViewport (l, b, w, h);
170 
171  if (properties_.glState().compatibilityProfile())
172  {
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  }
181 
182  ACG::GLState::disable(GL_BLEND);
183  ACG::GLState::enable(GL_DEPTH_TEST);
184  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
185  properties_.glState().pick_init (true);
186 
187  // do the picking
189  ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState() );
190 
191  // restore GL state
192  if (properties_.glState().compatibilityProfile())
193  {
194  glMatrixMode(GL_PROJECTION);
195  glLoadMatrixd(projection.get_raw_data());
196  glMatrixMode(GL_MODELVIEW);
197  glLoadMatrixd(modelview.get_raw_data());
198  ACG::GLState::enable(GL_LIGHTING);
199  }
200 
201  properties_.glState().set_clear_color (clear_color);
202 
203  if (properties_.glState().pick_error ())
204  {
205  if (pickCache_ && pickCache_->isBound ())
206  pickCache_->release ();
207 
208  std::cerr << "error - picking color stack invalid" << std::endl;
209  return -1;
210  }
211 
212  glPixelStorei(GL_PACK_ALIGNMENT, 1);
213  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
214 
215  // we can only read inside our viewport
216  if (x + 1 < w)
217  pW++;
218 
219  if (y + 1 < h)
220  pH++;
221 
222  if (x > 0)
223  {
224  x--;
225  pW++;
226  }
227  if (y > 0)
228  {
229  y--;
230  pH++;
231  }
232 
233  if (pH != 3 || pW != 3)
234  {
235  // initialize unused values with 0
236  for (int i = 0; i < 9; i++)
237  {
238  pixels[i][0] = 0;
239  pixels[i][1] = 0;
240  pixels[i][2] = 0;
241  pixels[i][3] = 0;
242  depths[i] = 0.0;
243  }
244  }
245 
246  // read from framebuffer
247  glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
248  glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths);
249 
250  // unbind pick cache
251  if (pickCache_ && pickCache_->isBound ())
252  {
253  pickCache_->release ();
254  updatePickCache_ = false;
255  pickCacheTarget_ = _pickTarget;
256  }
257 
258  // get first found pixel
259  for (int i = 0; i < 9; i++)
260  {
261  if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0))
262  {
263  hit = order[i];
264  break;
265  }
266  }
267 
268  if (hit < 0)
269  return 0;
270 
271 
272  ACG::Vec4uc rgba;
273  rgba[0] = pixels[hit][0];
274  rgba[1] = pixels[hit][1];
275  rgba[2] = pixels[hit][2];
276  rgba[3] = pixels[hit][3];
277 
278  std::vector<size_t> rv = properties_.glState().pick_color_to_stack (rgba);
279 
280  // something wrong with the color stack ?
281  if (rv.size () < 2)
282  {
283  std::cerr << "error - picking color not found in stack" << std::endl;
284  return -1;
285  }
286 
287  _nodeIdx = rv[1];
288  _targetIdx = rv[0];
289 
290 // // Debug Code to visualize picking cache ( DO NOT REMOVE!!!! Jan )
291 // QImage murks(glWidth (),glHeight (),QImage::Format_ARGB32);
292 // murks = pickCache_->toImage();
293 // for ( int i = 0 ; i < glWidth() ; ++i )
294 // for ( int j = 0 ; j < glHeight() ; ++j ) {
295 // QColor bla (murks.pixel(i,j));
296 // bla.setAlpha(255);
297 // murks.setPixel(i,j,bla.rgba());
298 // }
299 // murks.save("murks.png");
300 
301  if (_hitPointPtr)
302  {
303  *_hitPointPtr = properties_.glState().unproject (
304  ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit]));
305  }
306 
307  return 1;
308 }
309 
310 //-----------------------------------------------------------------------------
311 
313  const QPoint& _mousePos,
314  size_t& _nodeIdx,
315  size_t& _targetIdx,
316  ACG::Vec3d* _hitPointPtr )
317 {
318  // do we need an update?
320  pickCacheTarget_ != _pickTarget)
321  return -1;
322 
323  GLint x = _mousePos.x() - scenePos().x(),
324  y = glHeight() - (_mousePos.y() - scenePos().y()),
325  pW = 1,
326  pH = 1;
327  GLubyte pixels[9][4];
328  GLfloat depths[9];
329  int hit = -1;
330 
331  // traversing order (center, top, bottom, ...)
332  unsigned char order[9] = { 4, 7, 1, 3, 5, 0, 2, 6, 8 };
333 
334  // can't pick outside
335  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
336  return 0;
337 
338  // bind cache framebuffer object
339  pickCache_->bind ();
340 
341  glPixelStorei(GL_PACK_ALIGNMENT, 1);
342  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
343 
344  // we can only read inside our viewport
345  if (x + 1 < (int)glWidth ())
346  pW++;
347 
348  if (y + 1 < (int)glHeight ())
349  pH++;
350 
351  if (x > 0)
352  {
353  x--;
354  pW++;
355  }
356  if (y > 0)
357  {
358  y--;
359  pH++;
360  }
361 
362  if (pH != 3 || pW != 3)
363  {
364  // initialize unused values with 0
365  for (int i = 0; i < 9; i++)
366  {
367  pixels[i][0] = 0;
368  pixels[i][1] = 0;
369  pixels[i][2] = 0;
370  pixels[i][3] = 0;
371  depths[i] = 0.0;
372  }
373  }
374 
375  // read from framebuffer
376  glReadPixels (x, y, pW, pH, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
377  glReadPixels (x, y, pW, pH, GL_DEPTH_COMPONENT, GL_FLOAT, depths);
378 
379  // unbind
380  pickCache_->release ();
381 
382  // get first found pixel
383  for (int i = 0; i < 9; i++)
384  {
385  if (hit < 0 && (pixels[order[i]][2] != 0 || pixels[order[i]][1] != 0 || pixels[order[i]][0] != 0 || pixels[order[i]][3] != 0))
386  {
387  hit = order[i];
388  break;
389  }
390  }
391 
392  if (hit < 0)
393  return 0;
394 
395 
396  ACG::Vec4uc rgba;
397  rgba[0] = pixels[hit][0];
398  rgba[1] = pixels[hit][1];
399  rgba[2] = pixels[hit][2];
400  rgba[3] = pixels[hit][3];
401 
402  //initialize colorstack and translate color to index
403  properties_.glState().pick_init(true);
404  std::vector<size_t> rv = properties_.glState().pick_color_to_stack (rgba);
405 
406  // something wrong with the color stack ?
407  if (rv.size () < 2)
408  return -1;
409 
410  _nodeIdx = rv[1];
411  _targetIdx = rv[0];
412 
413  if (_hitPointPtr)
414  {
415  *_hitPointPtr = properties_.glState().unproject(
416  ACG::Vec3d(_mousePos.x(), scene()->height () - _mousePos.y(),depths[hit]));
417  }
418 
419  return 1;
420 }
421 
422 //-----------------------------------------------------------------------------
423 
425  const QRegion& _region,
426  QList<QPair<size_t, size_t> >& _list,
427  QVector<float>* _depths,
428  QVector<ACG::Vec3d>* _points)
429 {
430  QRect rect = _region.boundingRect();
431  GLint w = glWidth(),
432  h = glHeight(),
433  l = scenePos().x(),
434  b = scene()->height () - scenePos().y() - h,
435  x = rect.x(),
436  y = scene()->height () - rect.bottom();
437 
438  GLubyte* buffer = 0;
439  GLfloat* depths = 0;
440 
441 
442  // prepare GL state
443  makeCurrent();
444 
445 
447  {
448  // delete pick cache if the size changed
449  if (pickCache_ && pickCache_->size () != QSize (glWidth (), glHeight ()))
450  {
451  delete pickCache_;
452  pickCache_ = NULL;
453  }
454  // create a new pick cache frambuffer object
455  if (!pickCache_)
456  {
457  pickCache_ = new QFramebufferObject (glWidth (), glHeight (), QFramebufferObject::Depth);
458  if (!pickCache_->isValid ())
459  {
460  pickCacheSupported_ = false;
461  delete pickCache_;
462  pickCache_ = NULL;
463  }
464  }
465  if (pickCache_)
466  {
467  // the viewport for the framebuffer object
468  l = 0;
469  b = 0;
470  x = rect.x() - scenePos().x();
471  y = glHeight() - (rect.bottom() - scenePos().y());
472 
473  // we can only pick inside of our window
474  if (x < 0 || y < 0 || x >= (int)glWidth() || y >= (int)glHeight())
475  return 0;
476 
477  pickCache_->bind ();
478  }
479  }
480 
481  const ACG::GLMatrixd& modelview = properties_.glState().modelview();
482  const ACG::GLMatrixd& projection = properties_.glState().projection();
483 
484  ACG::Vec4f clear_color = properties_.glState().clear_color();
485  properties_.glState().set_clear_color (ACG::Vec4f (0.0, 0.0, 0.0, 0.0));
486 
487 
488  glViewport (l, b, w, h);
489  glMatrixMode(GL_PROJECTION);
490  glLoadIdentity();
491 
492  glMultMatrixd(projection.get_raw_data());
493  glMatrixMode(GL_MODELVIEW);
494  glLoadMatrixd(modelview.get_raw_data());
495  ACG::GLState::disable(GL_LIGHTING);
496  ACG::GLState::disable(GL_BLEND);
497  ACG::GLState::enable(GL_DEPTH_TEST);
498  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
499  properties_.glState().pick_init (true);
500 
501  // do the picking
503  ACG::SceneGraph::traverse_multipass(sceneGraphRoot_, action,properties_.glState());
504 
505  // restore GL state
506  glMatrixMode( GL_PROJECTION );
507  glLoadMatrixd(projection.get_raw_data());
508  glMatrixMode( GL_MODELVIEW );
509  glLoadMatrixd(modelview.get_raw_data());
510  ACG::GLState::enable(GL_LIGHTING);
511  ACG::GLState::enable(GL_BLEND);
512 
513  properties_.glState().set_clear_color(clear_color);
514 
515  if (properties_.glState().pick_error ())
516  {
517  if (pickCache_ && pickCache_->isBound ())
518  pickCache_->release ();
519  return false;
520  }
521 
522  buffer = new GLubyte[4 * rect.width() * rect.height()];
523 
524 
525 
526  glPixelStorei(GL_PACK_ALIGNMENT, 1);
527  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
528 
529  glReadPixels (x, y, rect.width(),
530  rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, buffer);
531 
532  if (_depths || _points ) {
533  depths = new GLfloat[ rect.width() * rect.height() ];
534  glReadPixels (x, y, rect.width(), rect.height(), GL_DEPTH_COMPONENT, GL_FLOAT, depths);
535 
536  /* Debug code, writing out the depth image
537  QImage depthmapimage(rect.width(), rect.height(), QImage::Format_Indexed8);
538 
539  // color map
540  for ( int i = 0 ; i <= 255 ; i++ )
541  depthmapimage.setColor( i, qRgb( i, i, i ) );
542 
543  for ( int i = 0 ; i < rect.width() ; i++ )
544  for ( int j = 0 ; j < rect.height() ; j++ )
545  {
546  depthmapimage.setPixel(i,rect.height()-j-1, (unsigned int)(depths[j*rect.width()+i]*255));
547  }
548 
549  depthmapimage.save("test.png");
550  */
551  }
552 
553  // Iterate over the bounding rectangle of the region
554  for (int y = 0; y < rect.height (); y++)
555  for (int x = 0; x < rect.width (); x++)
556  {
557 
558  // Check if the current point is in the polygon of the region
559  if (_region.contains (QPoint (rect.x() + x, rect.y() + y)))
560  {
561 
562  // Calculate position inside the buffer
563  const int bPos = (((rect.height () - (y + 1)) * rect.width ()) + x) * 4;
564 
565  // Get the picking color from the buffer at the current position
566  if (buffer[bPos + 2] != 0 || buffer[bPos + 1] != 0 || buffer[bPos] != 0 || buffer[bPos + 3] != 0)
567  {
568  ACG::Vec4uc rgba;
569  rgba[0] = buffer[bPos];
570  rgba[1] = buffer[bPos + 1];
571  rgba[2] = buffer[bPos + 2];
572  rgba[3] = buffer[bPos + 3];
573 
574  std::vector<size_t> rv = properties_.glState().pick_color_to_stack (rgba);
575  if (rv.size () < 2)
576  continue;
577 
578  QPair<size_t, size_t> curr(rv[1], rv[0]);
579 
580  // added a new (targetidx/nodeidx) pair
581  if( !_list.contains(curr))
582  {
583  _list << curr;
584 
585  if ( _depths || _points ) {
586 
587  const double curr_depth(depths[(rect.height()-(y+1))*rect.width() + x]);
588 
589  // If depths should be returned, we extract it here
590  if (_depths)
591  (*_depths) << curr_depth;
592 
593  // unproject depth to real (3D) depth value
594  if ( _points )
595  (*_points) << properties_.glState().unproject(ACG::Vec3d(x+rect.x(),h-(y+rect.y()),curr_depth));
596  }
597  }
598  }
599  }
600  }
601 
602  delete[] buffer;
603 
604  if ( _depths || _points )
605  delete[] depths;
606 
607  // unbind pick cache
608  if (pickCache_ && pickCache_->isBound ())
609  {
610  pickCache_->release ();
611  updatePickCache_ = false;
612  pickCacheTarget_ = _pickTarget;
613  }
614 
615  return true;
616 }
617 
618 
619 //-----------------------------------------------------------------------------
620 
621 bool
623 fast_pick( const QPoint& _mousePos,
624  ACG::Vec3d& _hitPoint )
625 {
626  // get x,y,z values of pixel
627  GLint x(_mousePos.x()), y(glHeight() - _mousePos.y());
628  GLfloat z;
629 
630 
631  makeCurrent();
632  glPixelStorei(GL_PACK_ALIGNMENT, 1);
633  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
634  glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
635 
636 
637  if (z < 0.99999)
638  {
639  _hitPoint = properties_.glState().unproject( ACG::Vec3d(x, y, z) );
640  return true;
641  }
642  else return false;
643 }
644 
645 
646 //=============================================================================
647 
648 //=============================================================================
std::vector< size_t > pick_color_to_stack(Vec4uc _rgba) const
Definition: GLState.cc:1104
bool fast_pick(const QPoint &_mousePos, ACG::Vec3d &_hitPoint)
bool pick_region(ACG::SceneGraph::PickTarget _pickTarget, const QRegion &_region, QList< QPair< size_t, size_t > > &_list, QVector< float > *_depths=0, QVector< ACG::Vec3d > *_points=0)
Perform picking action n a whole region.
const GLMatrixd & projection() const
get projection matrix
Definition: GLState.hh:786
QOpenGLFramebufferObject QFramebufferObject
Framebuffer object that holds the pick cache.
ACG::SceneGraph::PickTarget pickCacheTarget_
Pick target stored in pick cache.
const Vec4f & clear_color() const
get background color
Definition: GLState.hh:921
bool updatePickCache_
Should the pick cache be updated.
static void enable(GLenum _cap, bool _warnRemoved=true)
replaces glEnable, but supports locking
Definition: GLState.cc:1507
void pick_init(bool _color)
initialize name/color picking stack (like glInitNames())
Definition: GLState.cc:1043
Vec3d unproject(const Vec3d &_winPoint) const
unproject point in window coordinates _winPoint to world coordinates
Definition: GLState.cc:651
Viewer::ViewerProperties & properties_
All properties for this viewer.
bool pick_error() const
Definition: GLState.cc:1122
int pickColor(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
pick using colors
bool pickCacheSupported_
Is pick caching supported.
unsigned int glHeight() const
get height of QGLWidget
void set_clear_color(const Vec4f &_col)
set background color
Definition: GLState.cc:662
const Scalar * get_raw_data() const
Definition: Matrix4x4T.hh:256
int pickFromCache(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
pick from cache
const GLMatrixd & modelview() const
get modelview matrix
Definition: GLState.hh:791
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
QFramebufferObject * pickCache_
Framebuffer object that holds the pick cache.
PickTarget
What target to use for picking.
Definition: PickTarget.hh:73
virtual void makeCurrent()
Makes this widget the current widget for OpenGL operations.
void traverse_multipass(BaseNode *_node, Action &_action, const unsigned int &_pass)
Definition: SceneGraph.hh:254
ACG::GLState & glState()
Get the glState of the Viewer.
unsigned int glWidth() const
get width of QGLWidget
bool pick(ACG::SceneGraph::PickTarget _pickTarget, const QPoint &_mousePos, size_t &_nodeIdx, size_t &_targetIdx, ACG::Vec3d *_hitPointPtr=0)
static void disable(GLenum _cap, bool _warnRemoved=true)
replaces glDisable, but supports locking
Definition: GLState.cc:1527