I’ve been putting together a data visualizer, and had to implement picking. I’ve had good success with using query objects. Here’s a banged-together code snippet of all the pieces that I needed to get everything to work:
// Code to show how to use object queries to get picking behavior. For this code to work,
// You'll need to add your preferred rendering framework. This code was pulled from an
// FLTK framework I've been building for data visualization
// Class-wide global variables here for the sake of a compact file
// Classes that are not part of the OpenGl library are from the OpenGl SuperBible,
// which I highly reccomend: http://www.starstonesoftware.com/OpenGL/
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
bool isPicking;
// Constructor. Set up picking and the projection and model matrices
PickingExample::PickingExample(int x,int y,int w,int h,const char *l)
: Fl_Gl_Window(x,y,w,h,l)
{
isPicking = false;
pickQueryResult = READY;
glGenQueries(1, &pickQueryID);
if(pickQueryID == 0){
pickQueryResult = PICK_ERROR;
}
viewFrustum.SetPerspective(45.0f, (float)screenWidth/(float)screenHeight, 0.5f, 1000.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
}
// A slightly modified version of the original gluPickMatrix, taken from:
// http://oss.sgi.com/cgi-bin/cvsweb.cgi/projects/ogl-sample/main/gfx/lib/glu/libutil/project.c?rev=1.4;content-type=text%2Fplain
void PickingExample::setPickMatrix(GLdouble x, GLdouble y, GLdouble deltax, GLdouble deltay, GLint viewport[4])
{
if (deltax <= 0 || deltay <= 0) {
return;
}
projectionMatrix.LoadIdentity();
/* Translate and scale the picked region to the entire window */
GLfloat dx = (float)((viewport[2] - 2 * (x - viewport[0])) / deltax);
GLfloat dy = (float)((viewport[3] - 2 * (y - viewport[1])) / deltay);
projectionMatrix.Translate(dx, dy, 0);
projectionMatrix.Scale((float)(viewport[2] / deltax), (float)(viewport[3] / deltay), 1.0);
projectionMatrix.MultMatrix(viewFrustum.GetProjectionMatrix());
}
// the main drawing method. In this case, only drawing for picking is done, though it would
// be easy enough to glClear() afther the picking code, and draw everything over again with
// a normal perspective
void PickingExample::draw() {
GLint result;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
modelViewMatrix.PushMatrix();
// Have some Euler angle rotations and translations just to show how
// setPickMatrix() doesn't care about eye or model position
// global eye transformations
modelViewMatrix.Rotate(eyeOrient[1], 0, 1, 0);
modelViewMatrix.Rotate(eyeOrient[0], 1, 0, 0);
modelViewMatrix.Translate(eyePos[0], eyePos[1], eyePos[2]);
// global world transformations
modelViewMatrix.Translate(worldPos[0], worldPos[1], worldPos[2]);
modelViewMatrix.Rotate(worldOrient[0], 1, 0, 0);
modelViewMatrix.Rotate(worldOrient[1], 0, 1, 0);
// set up picking
if(isPicking && (pickQueryResult != PICK_ERROR))
projectionMatrix.PushMatrix();
// set the projection matrix so that it's just looking at a (in this case) 10x10 pixel area
// near the cursor. This method (shown above)
setPickMatrix(mouseX,viewport[3]-mouseY,10,10,viewport);
// for each item that you want to test, bracket it with a glBeginQuery()/glEndQuery()
// You'll need a unique pickQueryID for each model
glBeginQuery(GL_ANY_SAMPLES_PASSED, pickQueryID);
// render your model associated with pickQueryID here. It is assumed that
// the projection matrix and the model matrix will be handled by your shader(s)
// finish the query for this particular model
glEndQuery(GL_ANY_SAMPLES_PASSED);
projectionMatrix.PopMatrix();
// For each item that you queried above, see if at least one pixel was drawn
// Again, you'll need a unique pickQueryID for each model
glGetQueryObjectiv(pickQueryID, GL_QUERY_RESULT, &result);
if(result)
printf("hit
");
else
printf("miss
");
isPicking = false;
}
modelViewMatrix.PopMatrix();
}