color coded picking

I’m going to give color coding a try but can’t find any examples of this in my redbook. I have a few questions relating to the code below. Basically, I’m still trying to solve being able to highlight each individual triangle as the mouse hovers over it.

Previous ideas on how to do this … frame rendering loop:

  1. clear depth/color-buffer
    … I do this in my display
  2. render color-coded
    … I don’t know what this means. How is it color-coded? Before I draw the triangle, all I do is choose a unique color?
  3. read color of pixel of mouse w/ glReadPixel()
  4. clear depth/color buffer
    … Is all the above done in SELECT render mode?
  5. render model.
    … And then I switch to RENDER mode here?

My stuff looks like this …


public void display(GLAutoDrawable drawable) {
	// ...

	switch (getRenderState()) {
		case ModelConstants.UPDATE: // RENDER Mode
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

			// Projection transformation.				
			gl.glMatrixMode(GL.GL_PROJECTION);
			gl.glLoadIdentity();
			
			gl.glOrtho(-winWidth * getZoomFactor(), winWidth * getZoomFactor(), 
				-winHeight * getZoomFactor(), winHeight * getZoomFactor(), 100, -100);
					
			// reset the modelview matrix
			gl.glColor3f(1.0f, 1.0f, 1.0f);
			gl.glMatrixMode(GL.GL_MODELVIEW);
			gl.glLoadIdentity();

			// Viewing transformation.
			glu.gluLookAt(centroid.getX(), centroid.getY(), centroid.getMax_Z(),
				centroid.getX(), centroid.getY(), centroid.getZ(), 
				0.0, 1.0, 0.0);

			// Modeling tranformation.
			arcBall.getTransform().get(transf);
			gl.glMultMatrixf(transf);
							
			// Draw the model.
			{
				// eventually, calls drawByRegion below
				// to draw model.
				drawModel(drawable);
			}
			
			break;

		case ModelConstants.SELECT: // SELECT Mode
			// ... handle selection and picking

Added GL_COLOR_ARRAY here.

If each triangle has its own color (all 3 vertices have the same color), doesn’t that mean I need to draw by triangle using glDrawElements? I thought that’s part of what color-coding would move away from to help speed things up? Just a bit confused here. This is my code where I draw my model and works fine. Uses selection on groups of elements, but not per element.


public void drawByRegions(GL gl, int index, int activeId, Color color) {
	gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
	gl.glEnableClientState(GL.GL_COLOR_ARRAY);

	// ...
	vElementsBuffer[index].rewind();

	// highlights groups of elements.	
	gl.glLoadName(index);
	gl.glVertexPointer(3, GL.GL_FLOAT, 0, vElementsBuffer[index]);
	gl.glDrawArrays(GL.GL_TRIANGLES, 0, vElementsBuffer[index].capacity() / 3);

	gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
	gl.glDisableClientState(GL.GL_COLOR_ARRAY);

	return;
}

You can use OpenGL’s select render mode to do this type of selection. The general idea is that for each distinct object you place a distinct combination of integer values on the name stack. Afterwards OpenGL provides you with a set of hit records that each contain the values that were on the name stack. You can then use these values to determine which objects were selected. This is the same idea as color coded rendering, except your using stacks of integer values instead of colors. Chapter 13 of the red book explains all this quite well.

In his previous posts (leading up to this one) he talked about 10k - 100k poly models picked per triangle.

Color-coding is the only way to get decent performance out of this.

I’ll reply with more useful info tonight, as I’m at work now.

Hi,

I don’t have much time to answer your post but…
Basically you don’t use select mode anymore. You draw each triangle with a single color, to do that i suggest that you use COLOR_ARRAYS as you seem to try.
But i don’t see a Buffer with color information in it? When you have both vertex and color buffer you can draw the whole bunch of triangles, read the pixel under the mouse and retrieve the triangle. You certainly don’ t need to draw triangle 1 by 1.

Hope it helps…
Before Riven explains more.

Quite right…

You have your vertices in:
FloatBuffer vertexData

You have your colors in:
ByteBuffer colorData

Imagine you have 4 triangles, each triangle occupies 9 floats (3*3d vertex) in your vertexData. To make every triangle solid shaded, each vertex of it must have the same color. So you fill your colorData with 1 color per 3 vertices, then move on to the next. To be able to quickly identify which triangle was selected, it would be nice to not have FLOAT colors, but UNSIGNED_BYTE colors, to be able to work on bit-level easily later on.

triangle 0 [color = 0,0,0]
triangle 1 [color = 0,0,1]
triangle 2 [color = 0,0,2]
triangle 255 [color = 0,0,255]
triangle 256 [color = 0,1,0]
triangle 257 [color = 0,1,1]
triangle 258 [color = 0,1,2]
etc.

Now when you read back the pixel, you’ll get 3 bytes (RGB) which are quite easy to turn into the index again:
0,0,2 = (0 << 16) | (0 << 8) | (2 << 0) = 2
0,1,1 = (0 << 16) | (1 << 8) | (1 << 0) = 257
0,1,2 = (0 << 16) | (1 << 8) | (2 << 0) = 258


public final void renderColorCoded()
{
   glEnable(GL_VERTEX_ARRAY);
   glEnable(GL_COLOR_ARRAY);

   glVertexPointer(3, GL_FLOAT, 0, vertexData);
   glColorPointer(3, GL_UNSIGNED_BYTE, 0, colorData);
   glDrawArrays(GL_TRIANGLES, 0, vertexData.remaining() / 3);

   glDisable(GL_COLOR_ARRAY);
   glDisable(GL_VERTEX_ARRAY);
}

public final void renderNormally()
{
   glEnable(GL_VERTEX_ARRAY);

   glVertexPointer(3, GL_FLOAT, 0, vertexData);
   glDrawArrays(GL_TRIANGLES, 0, vertexData.remaining() / 3);

   glDisable(GL_VERTEX_ARRAY);
}

public final void renderFrame()
{
   // ... clear color+depth
   renderColorCoded();

   glReadPixels(x, y, 1, 1, ...);

   // ... clear color+depth
   renderNormally();


   // 
}

Not sure if I’m using glReadPixels, correctly, is this the correct way to call glReadPixels() when using the mouse to select an element? For a single pixel, the size should be 3, correct? And only the x, y of the mouse gets passed into glReadPixels x, y, width, and height? Is there a specific buffer I need to read from and use glReadBuffer()?

My app crashes the vm. If I remove glReadPixels(), my app doesn’t crash.


//constructor
MyModeler {
      mouseBuffer = BufferUtils.newByteBuffer(3);
}

// called per frame rendering within display() ...

mouseBuffer.rewind();

if (renderer.getMouseX() > 0 && renderer.getMouseY() > 0) {
       gl.glReadPixels(renderer.getMouseX(), renderer.getMouseY(),
       renderer.getMouseX(), renderer.getMouseY(),
       GL.GL_RGB, GL.GL_UNSIGNED_BYTE, mouseBuffer);
}

gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

Since you specified x and y coordinates for width and height, you get a returnbuffer a lot bigger than 3 bytes, which is likely the cause for your crash.

Try


       gl.glReadPixels(
              renderer.getMouseX(), renderer.getMouseY(), 1, 1
              GL.GL_RGB, GL.GL_UNSIGNED_BYTE,
              mouseBuffer
       );

Ok, I understood it wrong, thanks, that fixed the crash, but it still populates my mouseBuffer with 0’s immediately after the glReadPixels line. So something still isn’t right.


	mouseBuffer.rewind();

        // outputs all 0's
	while (mouseBuffer.position() < mouseBuffer.capacity()) {
		System.out.println("mouseBuffer: " + mouseBuffer.get());	
	}

Take into account that the y-axis is UP (not down, like J2D)

Just a few more questions cause I don’t see how going this route will solve my problems.

I don’t think I understand. I have my y axis as up in my glLookAt() method and I update my mouse x & y on mouseMoved() so I should be getting something in my buffer w/ 1 in the width and height of glReadPixels. Sometimes I see values, but most of the time, the returne RGB is 0 even when I’m hovering over a triangle that was color coded.

Another question I have about using this color coded method is below. My code looks like this.


gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
draw_ColorCoded(...);
glReadPixels(...);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
draw_Normally(...);

I still don’t see how this method works for me as I can’t highlight a single element visually in the model using the last method draw_Normally(…) which looks like below (and using glDrawElements … well, I might as well return to my old methods of bad performance).


		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
	
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
		
		vElementsBuffer[index].rewind();
		
		gl.glLoadName(index); // This loadName does selection on an entire group.
		gl.glVertexPointer(3, GL.GL_FLOAT, 0, vElementsBuffer[index]);
		gl.glDrawArrays(GL.GL_TRIANGLES, 0, vElementsBuffer[index].capacity() / 3);

		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);

Y-UP was meant as: the top of your screen has the highest Y value, and the bottom has 0. With your mouse it’s the opposite.

So to know which pixel you need to read:
int xFramebuffer = xMouse;
int yFramebuffer = displayHeight - yMouse;

Well, just imagine you can readback the pixel this way, then you can use the RGB values to find the INDEX of the triangle - as you encoded the RGB values in such a way that you can decode them back later. Now that you have the index of the triangle, render only that one, as a last step.

Problem solved :slight_smile:

And make SURE you set the POLYGON_MODE back to FILL, as otherwise your color-picking will not work 99% of the time (as you don’t fill your triangles) !!

At last! :smiley:

Pfftt, everything is so easy for you guys!

It was just a joke, and as the wise says : “Even the longest travel starts with a small step”.
Hope the color picking will solve your problem anyway… Keep us informed.

Well, it’s getting closer, at least now I understand what’s suppose to happen. I’m just trying to implement it efficiently now.

I’m having a hard time reading the color under the pointer. For some reason, it throws exceptions like the following when I mouse over my model and I can’t for anything figure why the color blue would read a -55. It does this quite a bit, throwing an exception because of blue being out of range. I’ve rechecked my color init method several times and the colors of my triangles RGB’s are all valid between 0 - 255.

I’m betting there’s a better way to do than what I’ve got below.


java.lang.IllegalArgumentException: Color parameter outside of expected range: Blue
        at java.awt.Color.testColorValueRange(Color.java:285)
        at java.awt.Color.<init>(Color.java:369)
        at java.awt.Color.<init>(Color.java:344)
        at gov.nasa.jsc.renderer.Modeler.readElementColor(Modeler.java:632)
        at gov.nasa.jsc.renderer.Modeler.drawRegions(Modeler.java:185)
        at gov.nasa.jsc.renderer.Renderer.drawModel(Renderer.java:613)
        at gov.nasa.jsc.renderer.Renderer.display(Renderer.java:245)

iae, r: 0, g: 30, b: -55


public void readElementColor(GLAutoDrawable drawable) {
	GL gl = drawable.getGL();

	mouseBuffer.clear();

	// getMouseY() is actually winHeight() - mouseY.
	gl.glReadPixels(renderer.getMouseX(), renderer.getMouseY(), 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, mouseBuffer);

	mouseBuffer.rewind();

	try {
		activeColor = new Color(mouseBuffer.get(0), mouseBuffer.get(1), mouseBuffer.get(2));

		System.out.println("activeColor: " + activeColor.toString() + ", hashCode: " + activeColor.hashCode());
	} 
	catch (IllegalArgumentException iae) {
		iae.printStackTrace();

		System.out.println("iae, r: " + mouseBuffer.get(0) + ", g: " +	mouseBuffer.get(1) + ", b: " + mouseBuffer.get(2));
	}

	return;
}

And then I redraw the single triangle here as a last step.


public void drawActiveElement(GL gl) {
	ActiveData elementData = null;

	if (colorMap.containsKey(activeColor.hashCode()) == false) {
		System.out.println("hashcode Not found.");
		return;
	}

	elementData = (ActiveData) colorMap.get(activeColor.hashCode());

	gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
	
	gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
	gl.glColor3f(1.0f, 1.0f, 1.0f);

	gl.glVertexPointer(3, GL.GL_FLOAT, 0, elementData.getNodeBuffer());
	gl.glDrawArrays(GL.GL_TRIANGLES, 0, elementData.getNodeBuffer().capacity());

	gl.glDisableClientState(GL.GL_VERTEX_ARRAY);

	return;
}

UNSIGNED_BYTE didn’t work for me, kept throwing IllegalArgumentExceptiosn when doing a call on glReadPixel, would fill my mouseBuffer w/ negative values. So I am using GL_BYTE which seems to work.

My hashCode idea for a key isn’t panning out very well so I’m back to trying to understand this idea. Each 3 of my vertices for a triangle have the same RGB value. What I don’t understand is how your using the index. Is that a String index value like “0,0,1” as a key to do a lookup? I store in a Hashtable a key w/ the object being the vertex array of 9 coord’s for my triangle. I don’t know why I can’t understand glDrawElements().

And of course I couldn’t get this to work either.

Can you help on this idea some more?

Other than these problems, I can see that my triangles are different colors and my mouse is reading them. It’s just storing/indexing and redrawing that last element that’s got me now.

Please use UNSIGNED_BYTE, you just have to convert them back through code:


ByteBuffer mouse = ...;
glReadPixels(x,y,1,1,mouse);
int r = mouse.get() & 0xFF; // this turns the byte into an unsigned byte (using the sign-bit as the ordinary 8th bit of an int: 32bit)
int g = mouse.get() & 0xFF;
int b = mouse.get() & 0xFF;

int triangleIndex = (r << 16) | (g <<< 8) | (b << 0); // decode the RGB to Index

indices.position(triangleIndex*3);
indices.limit(triangleIndex*3 + 3);
glDrawElements(....... paint 1 triangle...., indices);

It’s almost there. I beleive I’ve got this working and the performance really is much better, even though my triangles are not hightlighting properly, I can tell it functions close to what it should. I think my only problem left is that my triangles are colored wrong and I can’t tell where the problem is.

From one vertex (side) of the triangle it’s shaded darker/lighter and as you cross the triangle to another vertex, it gets lighter or darker. Some Triangles are all one color, but the colors of the multi-colored (shaded) triangles are all within the range that I thought I was setting my colors to.

I have a FloatBuffer triBuffer(3 * 3) per triangle that contains these coord values …
v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z

I have a ByteBuffer cBuffer(3 * 4) that contains int values of R, G, or B as such …
v1.r, v1.g, v2.b, v2.r, v2.g, v2.b, v3.r, v3.g, v3.b

Here is where I add my colors.


// stores a single triangle.
FloatBuffer triBuffer = BufferUtils.newFloatBuffer(3 * 3);

// stores colors for a single triangle.
// 3 colors and we store 32 bit ints for each color w/ putInt?
ByteBuffer cBuffer = BufferUtils.newByteBuffer(3 * 4);

for (all triangles) {
   elementBuffer[i] = BufferUtils.newFloatBuffer(region.size() * 9);

   // 1 color = 3 bytes per triangle ((3 * 3) *3).
   colorBuffer[i] = BufferUtils.newByteBuffer((region.size() * 9) * 3);

   indicesBuffer[i] = BufferUtils.newIntBuffer(region.size() * 3);


    for (each triangle) {
	int idx = 0;

	for (int i = 0; i < 3; i++) { // loop edges of a single triangle
		cBuffer.putInt(idx, getRed());
		triBuffer.put(idx++, (float) node.getX());
	
		cBuffer.putInt(idx, getGreen());
		triBuffer.put(idx++, (float) node.getY());

		cBuffer.putInt(idx, getBlue());
		triBuffer.put(idx++, (float) node.getZ());
	}

	colorBuffer[i].put(cBuffer);
	elementBuffer[i].put(triBuffer);
	indicesBuffer[i].put(index++);

	incrementColor();
    }

    i++;
}

And here I draw my color coded triangles.


public void drawByRegions_ColorCoded(GL gl, int index, int activeId, Color color) {
	gl.glEnableClientState(GL.GL_COLOR_ARRAY);
	gl.glEnableClientState(GL.GL_VERTEX_ARRAY);

	gl.glEnable(GL.GL_CULL_FACE);
	gl.glCullFace(GL.GL_BACK);
	gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);

	vElementsBuffer[index].rewind();
	colorBuffer[index].rewind();

	gl.glColorPointer(3, GL.GL_UNSIGNED_BYTE, 0, colorBuffer[index]);
	gl.glVertexPointer(3, GL.GL_FLOAT, 0, vElementsBuffer[index]);
	gl.glDrawArrays(GL.GL_TRIANGLES, 0, vElementsBuffer[index].capacity() / 3);

	gl.glDisable(GL.GL_CULL_FACE);
	gl.glDisableClientState(GL.GL_COLOR_ARRAY);
	gl.glDisableClientState(GL.GL_VERTEX_ARRAY);

	return;
}

Does anyone see anything wrong with what I’ve got there. I can’t tell why my colors are not a single solid color in each triangle. BTW - I just left off doing the glClear between drawing_colorCoded() and then doing a draw_Normally() within the same frame so I could see what was going on.

for (each triangle) {
int idx = 0;

for (int i = 0; i < 3; i++) { // loop edges of a single triangle
//
}

//
indicesBuffer[i].put(index++);

incrementColor();
    }

I think this should look like:

for (each triangle) {
int idx = 0;

for (int i = 0; i < 3; i++) { // loop edges of a single triangle
//
}

//
indicesBuffer[i].put(index++);
indicesBuffer[i].put(index++);
indicesBuffer[i].put(index++);

incrementColor();
    }

As you need 3 indices per triangle (3 vertices, so 3 references to them)

~~

This doesn’t explain your shading-problems though, as the above code would only render 1/3 of the model.

I’ll check it later, got to go now.