| Home | Documentation | Download | Screenshots | Developer |
Selection of objects of the scene using select() and an OpenGL GL_SELECT render mode.
Use the select() 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.
Analytic intersection computations are also possible once the screen coordinates have be converted
to a half line using convertClickToLine(). Make a selection and then move the camera
to see a representation of the intersection line.
#include "qglviewer.h"
class Viewer : public QGLViewer
{
protected :
void draw();
void mousePressEvent(QMouseEvent *e);
void select(QMouseEvent*);
QString helpString() const;
void init();
private :
qglviewer::Vec orig, dir, selectedPoint;
};
#include "select.h"
#include <math.h>
using namespace std;
// The id of the selected object. Should be encapsulated.
// -1 means no object is selected.
static int selected;
static void drawSpiral(const bool specialColor = false)
{
const float nbSteps = 100.0;
glBegin(GL_QUAD_STRIP);
for (float i=0; i<nbSteps; ++i)
{
if (specialColor)
glColor3f((nbSteps-i)/nbSteps, .8 , i/nbSteps/2.0);
else
glColor3f((nbSteps-i)/nbSteps, .2 , i/nbSteps);
float angle = i/4.0;
float c = cos(angle);
float s = sin(angle);
float r1 = 0.5 - i/(3.f*nbSteps);
float r2 = 0.3 - i/(3.f*nbSteps);
float alt = i/nbSteps - 0.5;
const float nor = .5;
const float up = sqrt(1.0-nor*nor);
glNormal3f(nor*c, nor*s, up);
glVertex3f(r1*c, r1*s, alt);
glVertex3f(r2*c, r2*s, alt+0.05);
}
glEnd();
}
static void drawScene(bool pushId = false)
{
// Draw the scene, with a possible pushName for selection
// Consider using several stack levels for different objects, or to separate
// the triangles, edges and vertices of the same object. Example :
// glPushName(0)
// for all triangles i, glPushName(i), draw triangle, glPopName()
// glPopName()
//
// glPushName(1)
// for all edges i, glPushName(i), draw edge, glPopName()
// glPopName()
//
// glPushName(2)
// for all vertex i, glPushName(i), draw vertex, glPopName()
// glPopName()
// As a result, you have a two level stack, with a type id (0,1 or 2 here)
// which indicates the type of the primitive, and then the id of the primitive.
// See the man page of glSelectBuffer() for details.
const int nb = 10;
for (int i=0; i<nb; ++i)
{
glPushMatrix();
glTranslatef(cos(2.0*i*M_PI/nb), sin(2.0*i*M_PI/nb), 0.);
if (pushId)
{
glPushName(i);
drawSpiral();
glPopName();
}
else
drawSpiral(i==selected);
glPopMatrix();
}
}
void Viewer::init()
{
restoreFromFile();
// Means no object is selected.
selected = -1;
glLineWidth(3.0);
glPointSize(10.0);
help();
}
void Viewer::draw()
{
drawScene();
// Draw the previous intersection line
glBegin(GL_LINES);
glVertex3fv(orig.address());
glVertex3fv((orig + 100.0*dir).address());
glEnd();
if (selected >= 0)
{
glColor3f(.9, .2, .1);
glBegin(GL_POINTS);
glVertex3fv(selectedPoint.address());
glEnd();
}
}
void Viewer::mousePressEvent(QMouseEvent *e)
{
if (((e->state() & ~Qt::MouseButtonMask) == Qt::ShiftButton) && (e->button() == Qt::LeftButton))
{
select(e);
updateGL();
}
else
QGLViewer::mousePressEvent(e);
}
void Viewer::select(QMouseEvent* e)
{
// Make openGL context current
makeCurrent();
const int SENSITIVITY = 4;
const int NB_HITS_MAX = 50;
// Prepare the selection mode
static GLuint hits[NB_HITS_MAX];
glSelectBuffer(NB_HITS_MAX, hits);
glRenderMode(GL_SELECT);
glInitNames();
// Loads the matrices
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(static_cast<GLdouble>(e->x()), static_cast<GLdouble>(viewport[3] - e->y()), SENSITIVITY, SENSITIVITY, viewport);
// Don't use loadProjectionMatrix() directly as it clears the GL_PROJECTION matrix with a glLoadIdentity.
// The false flag indicates that no glLoadIdentity should be called, in order to combine the matrices.
camera()->loadProjectionMatrix(false);
camera()->loadModelViewMatrix();
// Render scene with objects ids
drawScene(true);
glFlush();
// Get the results
GLint nb_hits = glRenderMode(GL_RENDER);
// Interpret results
unsigned int zMin = UINT_MAX;
selected = -1;
for (int i=0; i<nb_hits; ++i)
if (hits[i*4+1] < zMin)
{
zMin = hits[i*4+1];
selected = hits[i*4+3];
}
cout << nb_hits << " spiral" << ((nb_hits>1)?"s":"") << " under the cursor";
if (selected >= 0)
cout << ", selected = " << selected;
cout << endl;
// To draw a representation of the intersecting line
camera()->convertClickToLine(e->x(), e->y(), orig, dir);
cout << "e->x() = " << e->x() << endl;
cout << "e->y() = " << e->y() << endl;
bool found;
selectedPoint = camera()->pointUnderPixel(e->x(), e->y(), found);
selectedPoint -= 0.01*dir;
// if (found != (selected>=0)), it is because of SENSITIVITY.
}
QString Viewer::helpString() const
{
QString text("<h2>S e l e c t</h2>");
text += "Left click while pressing the <b>Shift</b> key to select an object of the scene.<br>";
text += "Selection is performed using the OpenGL <i>GL_SELECT</i> render mode. ";
text += "A line is drawn between the selected point and the camera selection position. ";
text += "using <i>convertClickToLine()</i>, a useful function for analytical intersections.<br>";
text += "Feel free to cut and paste this implementation in your own applications.";
return text;
}
#include "select.h"
#include <qapplication.h>
int main(int argc, char** argv)
{
// Read command lines arguments.
QApplication application(argc,argv);
// Instantiate the viewer.
Viewer v;
// Make the viewer window visible on screen.
v.show();
// Set the viewer as the application main widget.
application.setMainWidget(&v);
// Run main loop.
return application.exec();
}
Back to the main page