5

I'm already able to find the world coordinates of the place i clicked, and it also checks this with the depth buffer. For this is used the following code :

GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX,winY;
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

// obtain the Z position (not world coordinates but in range 0 - 1)
GLfloat z_cursor;
winX = (float)x_cursor;
winY = (float)viewport[3]-(float)y_cursor;
glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor);

// obtain the world coordinates
GLdouble x, y, z;
gluUnProject(winX, winY, z_cursor, modelview, projection, viewport, &x, &y, &z);

with x_cursor and y_cursor being my cursor coordinates.

So far so good and when printing x,y,z they seem to be good !

But now... After parsing my files containing all the polygons/triangles. I put everyting in openGL DISPLAY Lists. So my program basically just calls the lists, and translates/rotates them. Every object als has a unique name. So all i keep are the lists, I don't keep the points/triangles of every object

My Question :

So i have my world coordinates of where i clicked, but how can I figure out which object matches that position !?

Joseph
  • 51
  • 1
  • 3
  • What are "openGL Generic Lists"? – genpfault Aug 11 '11 at 15:40
  • If you kept the triangles and vertices associated with each object it would be trivial, right (find the triangle closest to `x,y,z` and return which object it belongs to)? Is there a reason why you don't keep them around? Another idea: assuming your objects are 'far enough' apart you could just check `x,y,z` against bounding volumes instead. @genpfault: I'm assuming he means display lists. – user786653 Aug 11 '11 at 15:43
  • Move away from display lists, use vertex arrays/buffers and keep a RAM copy of your mesh. Then all you got do do is implement some simple ray picking. – Christian Rau Aug 11 '11 at 15:48

4 Answers4

4

If you just want the object ID and are willing to tolerate 2x overdraw:

1) Clear color and depth buffers

2) Draw all objects with different solid colors (disable lighting and texturing), maintaining a color -> object ID map.

3) Read color buffer at mouse position, note color and look up in color map.

4) Clear color and depth buffers and re-render scene as normal.

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • 1
    This is a solution, but not an efficient one... Anyway thanks for sharing ! In the worst case i'll temporary solve it that way, so i can continue working :) Anyone else got a more efficient one ? – Joseph Aug 11 '11 at 15:47
  • @Joseph You won't get more efficient with your current display-list-only approach, without any RAM copies of your mesh data. What you can do is use a smaller framebuffer or simplified versions of your geometry in the ID pass (although without the mesh data, you won't be able to simplify them ;)). – Christian Rau Aug 11 '11 at 15:50
  • @Jospeh: More 'efficient' solutions (that don't require drawing the scene twice) are usually going to trade that GPU time for CPU time. Are you really sure you want that anyway? – user786653 Aug 11 '11 at 16:09
  • 2
    Can't shaders do this exact same thing, but into an additional output attribute, during the single rendering pass? – Ben Voigt Aug 11 '11 at 16:18
  • 2
    @Ben: Probably, though since the OP was using display lists I don't think they are anywhere near shaderland :) – genpfault Aug 11 '11 at 16:20
  • @Ben Voigt: And store it where that is accessible from the CPU? I'm sure that it is possible to have more output from shaders than RGBA + Z + Stencil, but I don't think it's going to be easier (Sure you can encode it in the alpha channel if you less than 255 objects, but that is a pretty ugly solution imo). – user786653 Aug 11 '11 at 16:22
1

I think you might be interested in: http://www.gpwiki.org/index.php/OpenGL:Tutorials:Picking

the_source
  • 648
  • 1
  • 6
  • 12
  • I already tried this one, but the problem with this one is that it gives back every hit in order of objects added. But i only need the front object (this ofcours depends on camera pos). Say a man standing behind the wall (2 objects) but the man is added first, then even though you don't see him, he be first encountered first according to the SELECTION_BUFFER, while the wall should be selected instead ... :( – Joseph Aug 11 '11 at 15:35
  • Aah I see your problem, I usually keep a copy on the heap of the world coordinates as well (CAD application). But now I'm also interested in a solution...! – the_source Aug 11 '11 at 15:39
  • @Joseph: hits in the selection buffer contain their min and max depth, so you can select the object closest to the camera by checking the min depth for the object. I'll add an example. – André Caron Aug 11 '11 at 16:32
0

Cast a ray into the scene from the point of view of the eye.

Compute the world space xyz location of the mouse click on the near plane by using vector arithmetic.

Here is a diagram of what you need to do:

enter image description here

This is more efficient than an unproject because unproject involves a matrix inverse (which is quite costly)

This is exactly the process you would use to cast a ray into a scene for a raytracer for the pixel that was clicked on. (In a ray tracer, you are retrieving the color, here you are retrieving the id of an object).

bobobobo
  • 64,917
  • 62
  • 258
  • 363
0

Refer to the OpenGL Picking Tutorial. It contains a fully working example. However, it seems that code can still be improved if you only want to select the object closest to the camera (e.g. the one painted on top). You can modify the code to get this result, however.

void list_hits(GLint hits, GLuint *names)
{
    int i; int bestmatch; GLuint bestdistance;
    if (hits < 1) {
      return;
    }
    bestmatch = 0;
    bestdistance = names[bestmatch*4+1]; // min distance to object.
    for (i = 1; i < hits; i++)
    {
        distance = names[i*4+1];
        if (distance < bestdistance) {
           bestmatch = i; bestdistance = distance;
        }
    }
    printf("Hit: %d.\n", bestmatch);
 }

Caveat: I've done this before, but this particular code is not tested.

André Caron
  • 44,541
  • 12
  • 67
  • 125