@stefan, yes, send me, with this method i'm only able to know what object is being selected but not in what part of it i'm clicking. although right now i only need to correct the perspective for simple selection, it can be useful to take a look.
@joshua, sure :)
so the idea is that whenever you receive a mouse click, or any other coordinate event you render again the whole scene with glRenderMode(GL_SELECT) but modifying the projection matrix so it only renders a 1x1 or the area you want around that coordinates with gluPickMatrix. that way every object that is rendered is detected and reported to be in that coordinates. this code should work for any perspective settings as i'm reading the current matrix and multiplying by that when i go to GL_SELECT:
void glSelect(int x, int y)
{
GLuint buff[512] = {0};
GLint hits, view[4];
GLfloat proj_matrix[16];
/*
This choose the buffer where store the values for the selection data
*/
glSelectBuffer(256, buff);
/*
This retrieves info about the viewport
*/
glGetIntegerv(GL_VIEWPORT, view);
glGetFloatv(GL_PROJECTION_MATRIX, proj_matrix);
/*
Switching in selecton mode
*/
glRenderMode(GL_SELECT);
/*
Clearing the names' stack
This stack contains all the info about the objects
*/
glInitNames();
/*
Now modify the viewing volume, restricting selection area around the cursor
*/
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
/*
restrict the draw to an area around the cursor
*/
gluPickMatrix(x, y, 1.0, 1.0, view);
glMultMatrixf(proj_matrix);
/*
Draw the objects onto the screen
*/
glMatrixMode(GL_MODELVIEW);
/*
draw only the names in the stack, and fill the array
*/
draw();
/*
Do you remeber? We do pushMatrix in PROJECTION mode
*/
glMatrixMode(GL_PROJECTION);
glPopMatrix();
/*
get number of objects drawed in that area
and return to render mode
*/
hits = glRenderMode(GL_RENDER);
/*
Print a list of the objects
*/
list_hits(hits, buff);
glMatrixMode(GL_MODELVIEW);
}
in mousePressed you call:
glSelect(x,ofGetHeight()-y); //the y is inversed as i'm not doing that in the matrix settings as it's done in oF
this function prints all the hits:
void list_hits(GLint hits, GLuint *buffer)
{
unsigned int j;
GLuint *ptr, minZ, minminZ, nearestId, *ptrNames, numberOfNames;
printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
minminZ = 0xffffffff;
for (int i = 0; i < hits; i++) {
numberOfNames = *ptr;
ptr++;
minZ = *ptr;
ptrNames = ptr+2;
if(minminZ>minZ && numberOfNames>0){
minminZ = minZ;
nearestId = ptrNames[0];
}
printf("%d names found:",numberOfNames);
for (j = 0; j < numberOfNames; j++,ptrNames++) {
printf ("%d ", *ptrNames);
}
printf ("\n");
ptr += numberOfNames+2;
}
printf("nearest id %d\n",nearestId);
printf ("\n");
}
and to give a name to the objects you have 2 possibilities:
glLoadName(GLint);
gives a name to everything you render from that point, you will need to add glPushName(0) to the previous code before the call to draw.
in my case i have several objects in a hierarchy so i want to distinguish what part of which object has been selected so i use the stack mode:
glPushName(id); //the id of the main object
//render something
glPushName(BUTTON_1);
button1.draw();
glPopName();
glPushName(BUTTON_2);
button2.draw();
glPopName();
....
glPopName();
this way when i return to GL_RENDER i get the id of the pressed object + the specific part of it.
also be careful if you do this in a thread (with tuio for example) as it will crash, so you will need to accumulate all the pressed events in a vector and process all of them in the update cycle.