picking object with vertex array

Started by
2 comments, last by ioda 19 years, 6 months ago
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.
Advertisement
Is it easier with gluUnProject() ?

And if yes how to do with gluUnProject()?

thx
nobody can help?
ok no more help needed, i found how to do with glunproject (with an article on nehe site)

This topic is closed to new replies.

Advertisement