|
Welcome to the Case Study 18: Simple OpenGL picking/selecting
Because of popular demand, here is the OpenGL picking/selecting case study.
Some information about picking: usually, you have to name each object is the scene (which is very helpful specially for 3D CAD applications) so that OpenGL can recognize what object it has encountered. Of course, if you have enough knowledge of math and proper collision detection, you can make your own picking/selection algorithm. If you wish to write you own, remember to shoot a ray from x0,y0,z0 to x0,y0,zN, which means that the ray must be shot in a perfect line from the screen so it can be more reliable from the user's perspective. But we will use OpenGL's method for fast picking.
Now lets get down to coding... First thing needed is a picking function:
int SelectObject(int x, int y)
{
int objectsFound = 0;int viewCoords[4] = {0};
unsigned int selectBuffer[32] = {0};
glSelectBuffer(32, selectBuffer); // Select the buffer
glGetIntegerv(GL_VIEWPORT, viewCoords);
glMatrixMode(GL_PROJECTION); // Switch to projection
glPushMatrix();
glRenderMode(GL_SELECT); // Selection mode
glLoadIdentity();
gluPickMatrix(x, viewCoords[3] - y, 2, 2, viewCoords);
gluPerspective(45.0f,SCREEN_WIDTH/SCREEN_HEIGHT,1,1000.0f);
glMatrixMode(GL_MODELVIEW);
RenderScene(); // RenderScene for select procedure
objectsFound = glRenderMode(GL_RENDER); // Get the number of objects
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
if (objectsFound > 0)
{
// Return the selected object depending on the lowestDepth...
unsigned int lowestDepth = selectBuffer[1];
int selectedObject = selectBuffer[3];
for(int i = 1; i < objectsFound; i++)
{if(selectBuffer[(i * 4) + 1] < lowestDepth)
{lowestDepth = selectBuffer[(i * 4) + 1];
selectedObject = selectBuffer[(i * 4) + 3];}}
return selectedObject;}return -1;
}
So basically, this function returns the current selected object's ID. Next, we go to the RenderScene function and setup the naming stack before anything gets drawn...
glInitNames();
glPushName(0);
The stack start from 0 now...
glPushMatrix();
glLoadName(someuniquename); // load a name for the object that must be rendered
DrawSomeObject();
It is quite important to add a unique name, so OpenGL can "see" the difference between 2 objects... Ok, the next thing to add is a Mouse Click event and a proper feedback from it. If you use the standard win32 API (or win64 lately ), just add...
case WM_LBUTTONDOWN:
int objectID = SelectObject(LOWORD(lParam), HIWORD(lParam));// get mouse X and Y position
if (objectID == SomeObject ) doSomething(objectID);// do something with that object... perhaps mark it as selected
}
...to the message pump.
Done? YES! It is that easy to select any object, thanks to the OpenGL API.
Hope you like the new site design. Credits go to me (FireStorm), GameTutorials, OpenGL and FireStorm srl.
THE END.  |