Jump to content


Picking Vertices


23 replies to this topic

#1 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 04 January 2007 - 06:43 PM

Please Help,

I am an OpenGL newbie and am having some difficulty using OpenGL to select specific vertices in a rendered image. First question, can Display Lists be used with OpenGL picking through a mouse?

I am rendering a model through Display Lists with a large amount of vertices (~58,000 points) and would like to know the easiest/most efficient way to allow the user to click on a specific point in the image with the mouse to select it.

Please help, I have received different info about using feedback mode or selection mode and need to know what is the best/most typical way when all I want to know is what vertex a user has selected with the mouse.

Some simple example code would be best, but at this point ANY hints would be greatly appreciated.

Thanks in advance.

#2 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 04 January 2007 - 07:07 PM

Unfortunately OpenGL doesn't provide a way of picking vertices
Here is a tutorial of what it does do

If you want to pick a point you need to draw a ray from the eye point projected through the cursor, and test to see if any vertices lie on that line. But because a vertex is a single point in space the chances of you actually being able to click an exact one are slim. You need to test against spheres round each vertex. The wiki has some info here


And 58k vertices sounds like a lot. You'll need to do some sort of spatial subdivision, such as, BSP tree or octree. Which method of subdivision is best? That depends on your data.

#3 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 04 January 2007 - 07:20 PM

Thank you, dave.

I appreciate the information.

#4 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 04 January 2007 - 10:39 PM

What about using gluUnProject() to change window coordinates gathered from my mouse to the Object coordinates in OpenGL? I could then compare the altered X, Y, and Z to those of the vertex list and select the closest one.

I know this sounds like more than a hack, but do you think it would be too slow?

Just an idea, since I am a newbie I am unsure as to the actual speed of the OpenGL calls for picking.


Thanks in advance.

#5 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 04 January 2007 - 11:20 PM

You mean gluProject dont you? You'd convert the X,Y,Z to a screen position then compare with the mouse. Its pretty expensive as you need to do a matrix*vector at least for each.

You could use gluUnproject to create the ray to test in 3D space. Thats a lot cheaper and you can do things to optimise it.

#6 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 05 January 2007 - 12:40 AM

Sorry for the typo.

You're right. The expense of using the gluProject would be too expensive.

#7 bombardier

    New Member

  • Members
  • Pip
  • 8 posts

Posted 05 January 2007 - 08:34 AM

Can't you use OpenGL selection while rendering vertices as GL_POINTs?

#8 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 05 January 2007 - 09:59 AM

an even better way, is to render the model to an offscreen texture in point list mode, use a diffuse colour that increments 1 per verex id, then under the mouse use a box to get contained id's.

that way you can grab any vertex that is visible on the screen.
pretty cool ay.

#9 bombardier

    New Member

  • Members
  • Pip
  • 8 posts

Posted 05 January 2007 - 10:31 AM

rouncer said:

an even better way, is to render the model to an offscreen texture in point list mode, use a diffuse colour that increments 1 per verex id, then under the mouse use a box to get contained id's.

that way you can grab any vertex that is visible on the screen.
pretty cool ay.
Isn't that the way OpenGL select works?

#10 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 05 January 2007 - 05:00 PM

bombardier said:

Can't you use OpenGL selection while rendering vertices as GL_POINTs?

I don't think so, maybe - I am new to OpenGL. I would certainly be open to the idea if I knew more of it. Can you explain in some kind of pseudo-code? This is all so new to me, sometimes it is even hard to know what to ask and I always appreciate any help I can get.

To help explain my problem, a snippet of my code is shown below (it is not exact because I am only trying to show the idea):
...
// The array of Triangles is read-in from a file:
int triangles[58,000];
// The array of Nodes (X, Y, Z Coordinates) are read-in from the same file
// and are used to indicate connectivity of each triangle:
// e.g., Triangle1 is made-up of Node1, Node2, and Node3
float nodes[26,000];

for (int i = 0; i < Number_of_Triangles; i++) {

// Get the Vector of node connections for this triangle
Vector nodeConns = triangles[i].getConnections();

glBegin(GL_TRIANGLE);
for (int j = 0; j < nodeConns.size(); j++) {
// Get the index of the node used in nodal connection
int nIDX = nodeConns.get(j);
glVertex3f(node[nIDX].getX(), node[nIDX].getY(), node[nIDX].getZ());
}
glEnd();
}
...

The above code is done to create the total image for a Display List. Any help/input/suggestion for how to select a given vertex/node using a mouse and OpenGL would be great.

Thanks.

#11 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 05 January 2007 - 05:20 PM

can you read of the picture you render?

#12 bombardier

    New Member

  • Members
  • Pip
  • 8 posts

Posted 05 January 2007 - 06:27 PM

In some project I handle left mouse button click event and execute something like this:


int SelectElement(const unsigned int cursorX, const unsigned int cursorY)

{

#define PICK_BUFF_SIZE	512


	GLuint	selectBuffer[PICK_BUFF_SIZE];

	GLint	viewport[4];


	glSelectBuffer(PICK_BUFF_SIZE, selectBuffer);

	glRenderMode(GL_SELECT);


	//	set projection matrix

	glMatrixMode(GL_PROJECTION);

	glPushMatrix();

	glLoadIdentity();


	glGetIntegerv(GL_VIEWPORT, viewport);

	gluPickMatrix(cursorX, cursorY, 10, 10, viewport);

	gluPerspective(45.0f, (float)viewport[2]/viewport[3], 0.01, 100);


	//	name rendering

	glInitNames();

	RenderNames();


	//	end picking

	glMatrixMode(GL_PROJECTION);

	glPopMatrix();

	glMatrixMode(GL_MODELVIEW);

	glFlush();


	// returning to normal rendering mode

	int hitCount = glRenderMode(GL_RENDER);


	// if there are hits process them

	int hit = -1;

	if (hitCount != 0)

	{

		hit = ProcessHits(hitCount, selectBuffer);

	}

	return hit;


#undef PICK_BUFF_SIZE

}



void RenderNames()

{

	//	set model transformation matrix

	glMatrixMode(GL_MODELVIEW);

	glPushMatrix();

	glMultMatrixf(modelMatrix);


	glPointSize(pointSize);	//	set some point size

	

	//	render all vertices with appropriate name sent to OpenGL

	//	name of each vertex will correspond to it's index in vertex array

	for(unsigned int i=0; i<vertexCount; i++)

	{

		glPushName(i);

		glBegin(GL_POINTS);

			glVertexV3(vertex[i].pos);

		glEnd();

		glPopName();

	}


	//	restore matrix

	glMatrixMode(GL_MODELVIEW);

	glPopMatrix();

}


The idea is to render vertices and tell OpenGL to associate name with each one. Name can be any number, but it seems really obvious to set that name to the vertex index number. On the beginning of the SelectElement function you set the viewport to render only small area around the mouse coordinates, so it will contain only vertices close to a location you clicked on. You can see what is going on if you remove
glRenderMode(GL_SELECT);
and try to execute SelectElement function from your game loop. It will only render small area around (cursorX, cursorY) screen position.

ProcessHit function processes entries in OpenGL select buffer, wich contains list of all the vertices rendered in the viewport, but I was only interested in first one.


unsigned int ProcessHits(const unsigned int hitCount, const GLuint *buffer) 

{

	unsigned int i;

	GLuint names, *ptr, minZ,*ptrNames, numberOfNames;


	ptr = (GLuint *) buffer;

	minZ = 0xffffffff;

	for (i = 0; i < hitCount; i++) {

		names = *ptr;

		ptr++;

		if (*ptr < minZ) {

			numberOfNames = names;

			minZ = *ptr;

			ptrNames = ptr+2;

		}


		ptr += names+2;

	}


	return (unsigned int) *ptrNames;

}


If you missed it before, dave_ sent you a nice link.

#13 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 05 January 2007 - 08:02 PM

that looks cool, but i do it a different way, your
not making a ray vector for christs sake, thats what
it looks like, correct me if im wrong.
you dont even need it.

-=-

just draw the points with a colour that is the same as
the index of its place in the array.
then you can select whatever you want from it.
its just on a texture remember.

#14 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 05 January 2007 - 08:06 PM

rouncer said:

that looks cool, but i do it a different way, your
not making a ray vector for christs sake, thats what
it looks like, correct me if im wrong.
you dont even need it.
-=-
just draw the points with a colour that is the same as
the index of its place in the array.
then you can select whatever you want from it.
its just on a texture remember.

Its the effectively the same as using the select buffer!

#15 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 05 January 2007 - 08:18 PM

Thanks to everybody. The help you guys provide newbies like myself is invaluable. Special thanks to bombarier, the code is great and helps a lot. I will give it a shot and see what I get. Dave, thanks for the link also -> great information.

#16 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2258 posts

Posted 05 January 2007 - 08:20 PM

why need a select buffer when its just image space
use, and its what shaders are meant for.
the select buffer in open gl just looks like it complicates
simple matters.

#17 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 06 January 2007 - 02:39 PM

Thanks to everyone.

I finally got it working with everyone's help :worthy: However, I just discovered another issue - probably simple but, being a newbie, I am still trying to figure it out. I am rotating, scaling, and transforming the image and when I do, selection no longer works.

Can anyone offer any ideas/hints as to why this would be occurring?

Below is a snippet of the code (Thanks to bombardier):

// Called when in SELECTION MODE

private void displaySelection(GL gl, GLU glu) {

  int[] viewport = new int[4];

  int hits = 0;

  gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);

  gl.glSelectBuffer(selectionBuffer.capacity(), selectionBuffer);

  gl.glRenderMode(GL.GL_SELECT);

  gl.glInitNames();                       

  gl.glMatrixMode(GL.GL_PROJECTION);

  gl.glPushMatrix();                  

  gl.glLoadIdentity();

  glu.gluPickMatrix((double)this.mouseX, (double)(viewport[3] - this.mouseY), 5.0d, 5.0d, viewport, 0);          

  glu.gluPerspective(45.0f, this.ratio, 1.0f, 1000.0f); 

  gl.glMatrixMode(GL.GL_MODELVIEW);

  // Render The NAMES for the Vertices 

  // on the NAME STACK

  renderNames(gl);

  gl.glMatrixMode(GL.GL_PROJECTION);      

  gl.glPopMatrix();                      

  gl.glMatrixMode(GL.GL_MODELVIEW);

  hits = gl.glRenderMode(GL.GL_RENDER); 

  mode = GL.GL_RENDER;                    

  // Process any hits that occured and were stored

 // in the selection Buffer

  processHits(hits, selectionBuffer);

} 


private void renderNames(GL gl) {

  // The "dm" Object is a Class that stores Data to be used for rendering

  // (such as Hashtables, Trees, lists, ...)

  Hashtable nodes = this.dm.getNodes();

  Enumeration enum = nodes.keys();

  gl.glPointSize(5.0f);

  while(enum.hasMoreElements()) {

      Integer nodeID = (Integer)enum.nextElement();

      Node node = (Node)nodes.get(nodeID);		

      gl.glPushName(nodeID.intValue());

      gl.glBegin(GL.GL_POINTS);

	gl.glVertex3f(node.getX(), node.getY(), node.getZ());

      gl.glEnd();

      gl.glPopName();

  }// End of WHILE-LOOP

}


public void processHits(int hits, IntBuffer buffer) {

  System.out.println("---------------------------------");

  System.out.println(" HITS: " + hits);

  int offset = 0;

  int names;

  float z1, z2;

  for (int i = 0; i < hits; i++) {

      System.out.println("- - - - - - - - - - - -");

      System.out.println(" hit: " + (i + 1));

      names = buffer.get(offset);

      offset++;	

      z1 = (float) buffer.get(offset) / 0x7fffffff;

      offset++;

      z2 = (float) buffer.get(offset) / 0x7fffffff;

      offset++;

      System.out.println(" number of names: " + names);

      System.out.println(" z1: " + z1);

      System.out.println(" z2: " + z2);

      System.out.println(" names: ");

      for (int j = 0; j < names; j++) {

          System.out.print("  " + buffer.get(offset));

          if (j == (names - 1)) {

                System.out.println("<-");

          } else {

                 System.out.println();

          }

       offset++;

       }// End of INNER FOR-LOOP

       System.out.println("- - - - - - - - - - - -");

    }// End of OUTER FOR-LOOP

    System.out.println("--------------------------------- ");

}

In just the GL_RENDER Mode, I am transforming, scaling, and rotating using code:

...

gl.glPushMatrix();

// Rotate around the X, Y, and Z axes

gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);

gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);

gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);


// "Center" the model

gl.glTranslatef(-this.dm.getCenterX(), -this.dm.getCenterY(), -this.dm.getCenterZ());

// Set the scaleing to reflect the ZOOM that

// the client has entered

gl.glScalef(this.zoom, this.zoom, this.zoom);


drawImage();


gl.glPopMatrix();

gl.glFlush();

...


Once again, thanks to everyone. I am learning so much about OpenGL thanks to you guys.

#18 rhhaney145

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 06 January 2007 - 03:29 PM

Never mind. I figured out the problem. It was simple (as I suspected). I just needed to apply the transformations, rotations, and scaling in GL_SELECT Mode same as in GL_RENDER Mode and make sure that I properly saved the ModelView Matrix.

I made the needed changes in the displaySelection() and renderNames():


private void displaySelection(GL gl, GLU glu) {

  int[] viewport = new int[4];

  int hits = 0;

  gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);

  gl.glSelectBuffer(selectionBuffer.capacity(), selectionBuffer);

  gl.glRenderMode(GL.GL_SELECT);

  gl.glInitNames();                       

  gl.glMatrixMode(GL.GL_PROJECTION);

  gl.glPushMatrix();

  gl.glLoadIdentity();		

  glu.gluPickMatrix((double)this.mouseX, (double)(viewport[3] - this.mouseY), 5.0d, 5.0d, viewport, 0);          

  glu.gluPerspective(45.0f, this.ratio, 1.0f, 1000.0f); 

  gl.glMatrixMode(GL.GL_MODELVIEW);

  // Had to ADD the following, to save the ModelView Matrix

  // PRIOR to rendering to the Name Stack via renderNames()

  gl.glPushMatrix();


  // Render The NAMES for the Vertices 

  // on the NAME STACK

  renderNames(gl);

  gl.glMatrixMode(GL.GL_PROJECTION);      

  gl.glPopMatrix();                      

  gl.glMatrixMode(GL.GL_MODELVIEW);

  // Had to ADD the following to Pop the Saved ModelView Matrix

  // back off the matrix stack

  gl.glPopMatrix();

  hits = gl.glRenderMode(GL.GL_RENDER); 

  mode = GL.GL_RENDER;                    

  processHits(hits, selectionBuffer);

} 


private void renderNames(GL gl) {

  // Had to ADD the rotations, transformations, and scaling here

  //

  // Rotate around the X, Y, and Z axes

  gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);

  gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);

  gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);


  // "Center" the model

  gl.glTranslatef(-this.dm.getCenterX(), -this.dm.getCenterY(), -this.dm.getCenterZ());

  // Set the scaleing to reflect the ZOOM that

  // the client has entered

  gl.glScalef(this.zoom, this.zoom, this.zoom);

  Hashtable nodes = this.dm.getNodes();

  Enumeration enum = nodes.keys();

  gl.glPointSize(5.0f);

  while(enum.hasMoreElements()) {

      Integer nodeID = (Integer)enum.nextElement();

      Node node = (Node)nodes.get(nodeID);

      gl.glPushName(nodeID.intValue());

      gl.glBegin(GL.GL_POINTS);

	gl.glVertex3f(node.getX(), node.getY(), node.getZ());

     gl.glEnd();			

     gl.glPopName();

   }// End of WHILE-LOOP

}

It can't be said enough, how much you guys help and support us newbies:worthy: Thanks for everything.

#19 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 309 posts

Posted 06 January 2007 - 03:56 PM

Hi,
I think at this point it would have been a lot simpler to go by Ray-Triangle intersection in the first place.
You would use the returned U and V baricentric coords to determine what Vertex (in the hit Triangle) is closer to the intersection point.
Also, no OpenGL knowledge is required to do it.

Ciao ciao : )
-Nautilus

(readin' this? perhaps you should get out more -- give it a thought)


#20 Reedbeta

    DevMaster Staff

  • Administrators
  • 4782 posts
  • LocationBellevue, WA

Posted 06 January 2007 - 07:20 PM

Simpler? Really? That code is pretty simple...writing a full ray-triangle intersection system with an octree or whatever would undoubtedly be faster, but it would require a bunch more code.
reedbeta.com - developer blog, OpenGL demos, and other projects





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users