picking object with vertex array
I render an object composed of about 520 000 triangles.
I would like to be able to clic on a triangle and know which one was choosen.
Here is my code used for this (done with visual c++).
// renderscene (i put only the part for selecting and not the part for rendering (the same without glInitNames(), glPop and glPush)
void CMeshDoc::RenderScene()
{
if(m_Light)
::glEnable(GL_LIGHTING);
else
::glDisable(GL_LIGHTING);
glInitNames(); // 1 Create empty name stack
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, his_file.pt_ptsGL);
glNormalPointer(GL_DOUBLE, 0, this_file.pt_normalsGL);
for(int i = 0; i < this_file.nb_triangles_reels; i++)
{
glPushName(i);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, this_file.pt_triangles.vertexIndices);
glPopName();
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
The functions for retrieving the number of the picked triangle :
int CMeshDoc::RetrieveObjectID(int x, int y)
{
int objectsFound = 0; // This will hold the amount of objects clicked
int viewportCoords[4] = {0}; // We need an array to hold our view port coordinates
// This will hold the ID's of the objects we click on.
// We make it an arbitrary number of 32 because openGL also stores other information
// that we don't care about. There is about 4 slots of info for every object ID taken up.
unsigned int selectBuffer[32] = {0};
// glSelectBuffer is what we register our selection buffer with. The first parameter
// is the size of our array. The next parameter is the buffer to store the information found.
// More information on the information that will be stored in selectBuffer is further below.
glSelectBuffer(32, selectBuffer); // Setup our selection buffer to accept object ID's
// This function returns information about many things in OpenGL. We pass in GL_VIEWPORT
// to get the view port coordinates. It saves it like a RECT with {top, left, bottom, right}
glGetIntegerv(GL_VIEWPORT, viewportCoords); // Get the current view port coordinates
// Now we want to get out of our GL_MODELVIEW matrix and start effecting our
// GL_PROJECTION matrix. This allows us to check our X and Y coords against 3D space.
glRenderMode(GL_SELECT); // Allows us to render the objects, but not change the frame buffer
glMatrixMode(GL_PROJECTION); // We want to now effect our projection matrix
glPushMatrix(); // We push on a new matrix so we don't effect our 3D projection
// This makes it so it doesn't change the frame buffer if we render into it, instead,
// a record of the names of primitives that would have been drawn if the render mode was
// GL_RENDER are now stored in the selection array (selectBuffer).
// glRenderMode(GL_SELECT); // Allows us to render the objects, but not change the frame buffer
glLoadIdentity(); // Reset our projection matrix
// gluPickMatrix allows us to create a projection matrix that is around our
// cursor. This basically only allows rendering in the region that we specify.
// If an object is rendered into that region, then it saves that objects ID for us (The magic).
// The first 2 parameters are the X and Y position to start from, then the next 2
// are the width and height of the region from the starting point. The last parameter is
// of course our view port coordinates. You will notice we subtract "y" from the
// BOTTOM view port coordinate. We do this to flip the Y coordinates around. The 0 y
// coordinate starts from the bottom, which is opposite to window's coordinates.
// We also give a 2 by 2 region to look for an object in. This can be changed to preference.
gluPickMatrix(x, viewportCoords[3] - y, 2, 2, viewportCoords);
// Next, we just call our normal gluPerspective() function, exactly as we did on startup.
// This is to multiply the perspective matrix by the pick matrix we created up above.
//gluPerspective(45.0f,(float)window_width/(float)window_height,0.1f,150.0f);
CRect rect;
AfxGetMainWnd()->GetClientRect(&rect);
double aspect = (rect.Height() == 0) ? rect.Width() : (double)rect.Width()/(double)rect.Height();
gluPerspective(45,aspect,0.1,10000.0);
glMatrixMode(GL_MODELVIEW); // Go back into our model view matrix
RenderScene(); // Now we render into our selective mode to pinpoint clicked objects
// If we return to our normal render mode from select mode, glRenderMode returns
// the number of objects that were found in our specified region (specified in gluPickMatrix())
objectsFound = glRenderMode(GL_RENDER); // Return to render mode and get the number of objects found
glMatrixMode(GL_PROJECTION); // Put our projection matrix back to normal.
glPopMatrix(); // Stop effecting our projection matrix
glMatrixMode(GL_MODELVIEW); // Go back to our normal model view matrix
// PHEW! That was some stuff confusing stuff. Now we are out of the clear and should have
// an ID of the object we clicked on. objectsFound should be at least 1 if we found an object.
if (objectsFound > 0)
{
// If we found more than one object, we need to check the depth values
// of all the objects found. The object with the LEAST depth value is
// the closest object that we clicked on. Depending on what you are doing,
// you might want ALL the objects that you clicked on (if some objects were
// behind the closest one), but for this tutorial we just care about the one
// in front. So, how do we get the depth value? Well, The selectionBuffer
// holds it. For every object there is 4 values. The first value is
// "the number of names in the name stack at the time of the event, followed
// by the minimum and maximum depth values of all vertices that hit since the
// previous event, then followed by the name stack contents, bottom name first." - MSDN
// The only ones we care about are the minimum depth value (the second value) and
// the object ID that was passed into glLoadName() (This is the fourth value).
// So, [0 - 3] is the first object's data, [4 - 7] is the second object's data, etc...
// Be carefull though, because if you are displaying 2D text in front, it will
// always find that as the lowest object. So make sure you disable text when
// rendering the screen for the object test. I use a flag for RenderScene().
// So, lets get the object with the lowest depth!
// Set the lowest depth to the first object to start it off.
// 1 is the first object's minimum Z value.
// We use an unsigned int so we don't get a warning with selectBuffer below.
unsigned int lowestDepth = selectBuffer[1];
// Set the selected object to the first object to start it off.
// 3 is the first object's object ID we passed into glLoadName().
int selectedObject = selectBuffer[3];
// Go through all of the objects found, but start at the second one
for(int i = 1; i < objectsFound; i++)
{
// Check if the current objects depth is lower than the current lowest
// Notice we times i by 4 (4 values for each object) and add 1 for the depth.
if(selectBuffer[(i * 4) + 1] < lowestDepth)
{
// Set the current lowest depth
lowestDepth = selectBuffer[(i * 4) + 1];
// Set the current object ID
selectedObject = selectBuffer[(i * 4) + 3];
}
}
// Return the selected object
return selectedObject;
}
// We didn't click on any objects so return 0
return 0;
}
and finaly (we pick when left button of the mouse is uped) :
void CMeshView::OnLButtonDown(UINT nFlags, CPoint point)
{
int objectID = 0;
objectID = GetDocument()->RetrieveObjectID(point.x, point.y);
CString szResult;
// to print the result (x and y are good but objectID is always 0)
szResult.Format( "clique sur le triangle numero %d, x = %d y = %d", objectID, point.x, point.y);
AfxMessageBox( szResult );
}
If you know what's wrong, thanks to tell me :-) .
Thanks in advance.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement