Nearest selection hit?

Started by
3 comments, last by Cacks 18 years, 1 month ago
Hi guys, How do I find the nearest selected primitive drawn from my hit records? Thanks for any help given!
Reject the basic asumption of civialisation especially the importance of material possessions
Advertisement
I have a full example of one way to do it here. The result that is returned is a vector of all object "selected", but the nearest object will be at index[0]. Hope that helps, good luck!
Could you explain the process of getting the nearest hit?
Reject the basic asumption of civialisation especially the importance of material possessions
Sure thing, I'll explain from the posted code.
std::vector<selectStruct> Select(int x, int y, int w, int h){	std::vector<selectStruct> hitList;

The first thing that I do is make a custom structure that will store the results of a selection. That is in case I ever need to get anything other than the closest object (imagine if you have a 2D selection box for a quasi-3D game and you wanted to select all units in an area).<hrsource] if(!w || !h)
return hitList;[/source]
If there is no width or height, then I simply return the empty hit list, since nothing was selected.
	GLuint buffer[512] = {0};										// Set Up A Selection Buffer	GLint hits = 0;												// The Number Of Objects That We Selected	// The Size Of The Viewport. [0] Is <x>, [1] Is <y>, [2] Is <length>, [3] Is <width>	GLint viewport[4] = {0};

Next, we have our 3 main variables. buffer stores all of the results from our hit testing function call. Hits returns the number of total selected objects and the viewport is the current window's viewport. The values are explained in the comment.
	// This Sets The Array <viewport> To The Size And Location Of The Screen Relative To The Window	glGetIntegerv(GL_VIEWPORT, viewport);	glSelectBuffer(512, buffer);								// Tell OpenGL To Use Our Array For Selection

Now, we will want to store the current window's viewport into our array as well as tell OpenGL to use our seleciton buffer as in where to store the results. We use 512 because that is the size of our array, which should be more than enough (128 total objects at any given time, since each object uses 4 spaces)
	// Puts OpenGL In Selection Mode. Nothing Will Be Drawn.  Object ID's and Extents Are Stored In The Buffer.	glRenderMode(GL_SELECT);	glInitNames();												// Initializes The Name Stack	glPushName(0);												// Push 0 (At Least One Entry) Onto The Stack

Now, we will call glRenderMode with GL_SELECT. What that means is nothing is drawn to the screen. Since selection involves the use of "names" which are integer ids telling you what object is what, you then have to call glInitNames to start the process. We call glPushName to push one entry on the stack.
	glMatrixMode(GL_PROJECTION);								// Selects The Projection Matrix	glPushMatrix();												// Push The Projection Matrix	glLoadIdentity();											// Resets The Matrix

The next thing you will want to do is reset the projection matrix after saving it to the stack. Since we want to essentially recreate the current scene, we have to start as if we are doing a normal render, except this time, nothing is actually drawn.
	// This Creates A Matrix That Will Zoom Up To A Small Portion Of The Screen, Where The Mouse Is.	double cx = x + ((double)w / 2.0f);	double cy = (viewport[3] - y) - ((double)h / 2.0f);	gluPickMatrix(cx, cy, (double)w, (double)h, viewport);

This segment will take the current mouse position and generate the correct data needed to use in the gluPickMatrix function. The goal of that function is to essentiall restrict rendering to a specific region, which in our case is the selection box. We do not care about objects outside of the box, so that is why this function has to be used
	// Apply The Perspective Matrix	gluPerspective(45.0f, (GLfloat) (viewport[2]-viewport[0])/(GLfloat) (viewport[3]-viewport[1]), 0.1f, 100.0f);	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer	glLoadIdentity();									// Reset The View	// Draw only objects that can be selected here, will	// need to a nice little system to do that, do not hard code it in a game	DrawBox(true);	glMatrixMode(GL_PROJECTION);								// Select The Projection Matrix	glPopMatrix();												// Pop The Projection Matrix	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix

In this large segment, all we do is setup the scene and draw it. Where the comments are, your drawing code goes for all objects that you need to be selected. Each object *should* call glLoadName(X); to load itself into the selection logic if you want that object to be selectable. Each object must have a unique ID to be seperate from others.
	hits = glRenderMode(GL_RENDER);								// Switch To Render Mode, Find Out How Many

Once we have drawn all of the objects for selection, we complete the process by changing the render mode so it renders everything and then "picks" the appropriate objects.
	// Objects Were Drawn Where The Mouse Was	if (hits > 0)	{		GLdouble modelMatrix[16];		glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);		GLdouble projMatrix[16];		glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);		int viewport2[4];		glGetIntegerv(GL_VIEWPORT, viewport2);		double objX, objY, objZ;		for (int loop = 0; loop < hits; loop++)			{			gluUnProject(cx, cy, .5, modelMatrix, projMatrix, viewport2, &objX, &objY, &objZ);			hitList.push_back(selectStruct(buffer[(loop * 4) + 3], objX, objY, objZ));		}		// Sort the list so the closest object is the first item		std::sort(hitList.begin(), hitList.end());	}	return hitList;}

The rest of this function analyzes and stores the results from the selection process. gluUnProject is used to get the object's X, Y, and Z's positions when they are selected. I choose to do it this way to know actual distances the selection is. Most other examples will just return the raw data. I finally sort the results so I know that the object at index 0 is the 'closest'.

Hopefully you can get a better idea of what to do now. I am not an OpenGL expert, so if anyone that knows it sees flaws in my explanation, feel free to point out the corrections [smile] For an alternative method that is similar but does less, take a look here. Not sure if it 'works' (I couldn't get it to, hence I wrote my own from reading the docs and MSDN [wink]) but at the end you will see the traditional way to return data.
Hey,

thanks for the help, I have it working now!
Reject the basic asumption of civialisation especially the importance of material possessions

This topic is closed to new replies.

Advertisement