<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>libQGLViewer select example</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <link href="../qglviewer.css" rel="stylesheet" type="text/css" /> <link rel="shortcut icon" href="../images/qglviewer.ico" type="image/x-icon" /> <link rel="icon" href="../images/qglviewer.icon.png" type="image/png" /> </head> <body> <table class="banner"> <tr> <td align="center"><a href="../index.html"><b>Home</b></a></td> <td align="center"><a href="../refManual/hierarchy.html"><b>Documentation</b></a></td> <td align="center"><a href="../download.html"><b>Download</b></a></td> <td align="center" class="qindexHL"><a href="index.html"><b>Screenshots</b></a></td> <td align="center"><a href="../developer.html"><b>Developer</b></a></td> </tr> </table> <h1>The select example</h1> <center> <img src="../images/select.jpg" width="300" height="200" alt="select"/> </center> <p> Selection of objects of the scene using <code>select()</code> and an OpenGL GL_SELECT render mode. </p> <p> Use the <code>select()</code> callback function to implement your object selection function. This examples is based on a generic GL_SELECT implementation that can easily be cut and pasted in your applications. </p> <p> Analytic intersection computations are also possible once the screen coordinates have be converted to a half line using <code>convertClickToLine()</code>. Make a selection and then move the camera to see a representation of the intersection line. </p> <h2>select.h</h2> <pre> <span class="dir">#include <QGLViewer/qglviewer.h> </span> <span class="key">class </span>Viewer : <span class="key">public </span>QGLViewer { <span class="key">protected </span>: <span class="key">virtual </span><span class="typ">void </span>draw(); <span class="key">virtual </span><span class="typ">void </span>select(<span class="typ">const </span>QMouseEvent*); <span class="key">virtual </span><span class="typ">void </span>init(); <span class="key">virtual </span>QString helpString() <span class="typ">const</span>; <span class="key">private </span>: qglviewer::Vec orig, dir, selectedPoint; };</pre> <h2>select.cpp</h2> <pre> <span class="dir">#include </span><span class="dstr">"select.h"</span><span class="dir"> </span><span class="dir">#include <math.h> </span><span class="dir">#include <limits.h> </span><span class="com">// UINT_MAX </span><span class="dir"></span> <span class="key">using namespace </span>std; <span class="com">// The id of the selected object. Should be encapsulated. </span><span class="com">// -1 means no object is selected. </span><span class="typ">static int </span>selected; <span class="typ">static void </span>drawSpiral(<span class="typ">const bool </span>specialColor = <span class="key">false</span>) { <span class="typ">const float </span>nbSteps = <span class="num">100.0</span>; glBegin(GL_QUAD_STRIP); <span class="key">for </span>(<span class="typ">float </span>i=<span class="num">0</span>; i<nbSteps; ++i) { <span class="key">if </span>(specialColor) glColor3f((nbSteps-i)/nbSteps, .<span class="num">8 </span>, i/nbSteps/<span class="num">2.0</span>); <span class="key">else </span>glColor3f((nbSteps-i)/nbSteps, .<span class="num">2 </span>, i/nbSteps); <span class="typ">float </span>angle = i/<span class="num">4.0</span>; <span class="typ">float </span>c = cos(angle); <span class="typ">float </span>s = sin(angle); <span class="typ">float </span>r1 = <span class="num">0.5 </span>- i/(<span class="num">3.</span>f*nbSteps); <span class="typ">float </span>r2 = <span class="num">0.3 </span>- i/(<span class="num">3.</span>f*nbSteps); <span class="typ">float </span>alt = i/nbSteps <span class="num">- 0.5</span>; <span class="typ">const float </span>nor = .<span class="num">5</span>; <span class="typ">const float </span>up = sqrt(<span class="num">1.0</span>-nor*nor); glNormal3f(nor*c, nor*s, up); glVertex3f(r1*c, r1*s, alt); glVertex3f(r2*c, r2*s, alt+<span class="num">0.05</span>); } glEnd(); } <span class="typ">static void </span>drawScene(<span class="typ">bool </span>pushId = <span class="key">false</span>) { <span class="com">// Draw the scene, with a possible pushName() for selection </span> <span class="com">// Consider using several stack levels for different objects, or to separate </span> <span class="com">// the triangles, edges and vertices of the same object. Example : </span> <span class="com">// glPushName(0) </span> <span class="com">// for all triangles i, glPushName(i), draw triangle, glPopName() </span> <span class="com">// glPopName() </span> <span class="com">// </span> <span class="com">// glPushName(1) </span> <span class="com">// for all edges i, glPushName(i), draw edge, glPopName() </span> <span class="com">// glPopName() </span> <span class="com">// </span> <span class="com">// glPushName(2) </span> <span class="com">// for all vertex i, glPushName(i), raster vertex, glPopName() </span> <span class="com">// glPopName() </span> <span class="com">// As a result, you have a two level stack, with a type id (0, 1 or 2 here) </span> <span class="com">// which indicates the type of the primitive, and then the id of the primitive. </span> <span class="com">// See the man page of glSelectBuffer() for details. </span> <span class="typ">const int </span>nb = <span class="num">10</span>; <span class="key">for </span>(<span class="typ">int </span>i=<span class="num">0</span>; i<nb; ++i) { glPushMatrix(); glTranslatef(cos(<span class="num">2.0</span>*i*M_PI/nb), sin(<span class="num">2.0</span>*i*M_PI/nb), <span class="num">0.</span>); <span class="key">if </span>(pushId) { glPushName(i); drawSpiral(); glPopName(); } <span class="key">else </span>drawSpiral(i==selected); glPopMatrix(); } } <span class="typ">void </span>Viewer::select(<span class="typ">const </span>QMouseEvent* e) { <span class="com">// You should cut-paste this rather tricky function as your own "select()" function. </span> <span class="com">// You need a drawing function that pushes and pops names to tag objects' id (see drawSpiral() above). </span> <span class="com">// You have to change the call to the drawWithPush (here a simple drawScene()). </span> <span class="com">// "selected" is set to the closest object that was picked (or -1 if no object was selected) </span> <span class="com">// Make openGL context current </span> makeCurrent(); <span class="typ">const int </span>SENSITIVITY = <span class="num">4</span>; <span class="typ">const int </span>NB_HITS_MAX = <span class="num">64</span>; <span class="com">// Prepare the selection mode </span> <span class="typ">static </span>GLuint hits[NB_HITS_MAX]; glSelectBuffer(NB_HITS_MAX, hits); glRenderMode(GL_SELECT); glInitNames(); <span class="com">// Loads the matrices </span> glMatrixMode(GL_PROJECTION); glLoadIdentity(); GLint viewport[<span class="num">4</span>]; camera()->getViewport(viewport); gluPickMatrix(<span class="key">static_cast</span><GLdouble>(e->x()), <span class="key">static_cast</span><GLdouble>(e->y()), SENSITIVITY, SENSITIVITY, viewport); <span class="com">// loadProjectionMatrix() first resets the GL_PROJECTION matrix with a glLoadIdentity. </span> <span class="com">// Give false as a parameter in order to prevent this and to combine the matrices. </span> camera()->loadProjectionMatrix(<span class="key">false</span>); camera()->loadModelViewMatrix(); <span class="com">// Render scene with objects ids. </span> <span class="com">// Change here to call your own "drawWithPush()" function. </span> drawScene(<span class="key">true</span>); glFlush(); <span class="com">// Get the number of objects were seen through the pick matrix frustum. Reset GL_RENDER mode. </span> GLint nb_hits = glRenderMode(GL_RENDER); <span class="com">// Compute orig and dir, used to draw a representation of the intersecting line </span> camera()->convertClickToLine(e->x(), e->y(), orig, dir); <span class="com">// Display of selection results </span> cout << <span class="str">"("</span> << e->x() << <span class="str">","</span> << e->y() << <span class="str">") : "</span>; <span class="key">if </span>(nb_hits <= <span class="num">0</span>) { selected = <span class="num">-1</span>; cout << <span class="str">"No object selected"</span> << endl; <span class="key">return</span>; } cout << nb_hits << <span class="str">" spiral"</span> << ((nb_hits><span class="num">1</span>)?<span class="str">"s"</span>:<span class="str">""</span>) << <span class="str">" under the cursor"</span> << endl; <span class="com">// Interpret results : each object created 4 values in the "hits" array : </span> <span class="com">// hits[4*i+1] is the object minimum depth value, while hits[4*i+3] is the id pushed on the stack. </span> <span class="com">// Of all the objects that were projected in the pick region, we select the closest one (zMin comparaison). </span> <span class="com">// This code needs to be modified if you use several stack levels. See glSelectBuffer() man page. </span> <span class="typ">unsigned int </span>zMin = hits[<span class="num">1</span>]; selected = hits[<span class="num">3</span>]; <span class="key">for </span>(<span class="typ">int </span>i=<span class="num">1</span>; i<nb_hits; ++i) <span class="key">if </span>(hits[i*<span class="num">4</span>+<span class="num">1</span>] < zMin) { zMin = hits[i*<span class="num">4</span>+<span class="num">1</span>]; selected = hits[i*<span class="num">4</span>+<span class="num">3</span>]; } <span class="com">// Find the selectedPoint coordinates, using camera()->pointUnderPixel(). </span> <span class="typ">bool </span>found; selectedPoint = camera()->pointUnderPixel(e->x(), e->y(), found); selectedPoint -= <span class="num">0.01</span>*dir; <span class="com">// Small offset to make point clearly visible. </span> <span class="com">// Note that "found" is different from (selected>=0) because of SENSITIVITY. </span>} <span class="typ">void </span>Viewer::init() { restoreFromFile(); <span class="com">// Means no object is selected. </span> selected = <span class="num">-1</span>; glLineWidth(<span class="num">3.0</span>); glPointSize(<span class="num">10.0</span>); help(); } <span class="typ">void </span>Viewer::draw() { drawScene(); <span class="com">// Draw the previous intersection line </span> glBegin(GL_LINES); glVertex3fv(orig.address()); glVertex3fv((orig + <span class="num">100.0</span>*dir).address()); glEnd(); <span class="key">if </span>(selected >= <span class="num">0</span>) { glColor3f(.<span class="num">9</span>, .<span class="num">2</span>, .<span class="num">1</span>); glBegin(GL_POINTS); glVertex3fv(selectedPoint.address()); glEnd(); } } QString Viewer::helpString() <span class="typ">const </span>{ QString text(<span class="str">"<h2>S e l e c t</h2>"</span>); text += <span class="str">"Left click while pressing the <b>Shift</b> key to select an object of the scene.<br>"</span>; text += <span class="str">"Selection is performed using the OpenGL <i>GL_SELECT</i> render mode. "</span>; text += <span class="str">"A line is drawn between the selected point and the camera selection position. "</span>; text += <span class="str">"using <i>convertClickToLine()</i>, a useful function for analytical intersections.<br>"</span>; text += <span class="str">"Feel free to cut and paste this implementation in your own applications."</span>; <span class="key">return </span>text; }</pre> <h2>main.cpp</h2> <pre> <span class="dir">#include </span><span class="dstr">"select.h"</span><span class="dir"> </span><span class="dir">#include <qapplication.h> </span> <span class="typ">int </span>main(<span class="typ">int </span>argc, <span class="typ">char</span>** argv) { <span class="com">// Read command lines arguments. </span> QApplication application(argc,argv); <span class="com">// Instantiate the viewer. </span> Viewer v; <span class="com">// Make the viewer window visible on screen. </span> v.show(); <span class="com">// Set the viewer as the application main widget. </span> application.setMainWidget(&v); <span class="com">// Run main loop. </span> <span class="key">return </span>application.exec(); }</pre> <p> <a href="index.html">Go back</a> to the examples main page </p> <p> <a href="http://validator.w3.org/check/referer"><img src="../images/xhtml.png" alt="Valid XHTML 1.0!" height="31" width="88" border="0"/></a> <a href="http://jigsaw.w3.org/css-validator/check/referer"><img src="../images/css.png" width="88" height="31" alt="Valid CSS!" border="0"/></a> <i>Last modified on Thursday, February 5, 2004.</i> </p> </body> </html>