tags:

views:

97

answers:

3

I am creating a simple 2D OpenGL game, and I need to know when the player clicks or mouses over an OpenGL primitive. (For example, on a GL_QUADS that serves as one of the tiles...) There doesn't seems to be a simple way to do this beyond brute force or opengl.org's suggestion of using a unique color for every one of my primitives, which seems a little hacky. Am I missing something? Thanks...

+4  A: 

My advice, don't use OpenGL's selection mode or OpenGL rendering (brute force method you are talking about), use a CPU-based ray picking algorithm if 3D. For 2D, like in your case, it should be straightforward, it's just a test to know if a 2D point is in a 2D rectangle.

Stringer Bell
It is easy to tell if a 2D point is in a 2D rect, but what I need to know is which rect, out of 500 or so, is my mouse cursor inside of without iterating over them all each frame. (or am I missing from your answer?)
Soren Johnson
Ah, yes you are right. A common pattern is to maintain a space partionning of your scene primitives, a 2D-Quadtree should do the trick.
Stringer Bell
+1 for Quadtrees. There are a bunch of good data structures for keeping track of this sort of thing.
Paul McMillan
+1  A: 

I would suggest to use the hacky method if you want a quick implementation (coding time, I mean). Especially if you don't want to implement a quadtree with moving ojects. If you are using opengl immediate mode, that should be straightforward:

// Rendering part
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
for(unsigned i=0; i<tileCout; ++i){
    unsigned tileId = i+1; // we inc the tile ID in order not to pick up the black
    glColor3ub(tileId &0xFF, (tileId >>8)&0xFF, (tileId >>16)&0xFF);
    renderTileWithoutColorNorTextures(i);
}

// Let's retrieve the tile ID
unsigned tileId = 0;
glReadPixels(mouseX, mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
             (unsigned char *)&tileId);
if(tileId!=0){  // if we didn't picked the black 
    tileId--;
    // we picked the tile number tileId
}

// We don't want to show that to the user, so we clean the screen
glClearColor(...); // the color you want
glClear(GL_COLOR_BUFFER_BIT);

// Now, render your real scene
// ...
// And we swap
whateverSwapBuffers(); // might be glutSwapBuffers, glx, ...
tibur
I still don't quite understand - eventually, I will need to render a color, right? Won't that color override the "fake" color we are using for picking?
Soren Johnson
Well. I forgot to mention that you don't want to show that fake image to the screen. After retrieving the picked tile, don't swap the back buffer but wipe it using `glClear` function. Code updated.
tibur
+1  A: 

You can use OpenGL's glRenderMode(GL_SELECT) mode. Here is some code that uses it, and it should be easy to follow (look for the _pick method)

(and here's the same code using GL_SELECT in C)

(There have been cases - in the past - of GL_SELECT being deliberately slowed down on 'non-workstation' cards in order to discourage CAD and modeling users from buying consumer 3D cards; that ought to be a bad habit of the past that ATI and NVidia have grown out of ;) )

Will
I've worked in CAD industry during last four years and haven't heard of any real world software that make use of OpenGL's selection mode, seriously. At least not CATIA and SolidWorks or everything from Autodesk. I'm pretty sure everybody is using CPU-based picking (way way much faster). Moreover, selection mode has been marked as deprecated. I wouldn't use it for a professional application, games included.
Stringer Bell