OpenGL Core Profile and Mouse Picking

I'm working on a Rubik's Cube type twisty puzzle simulation. The puzzle consists of an octahedron which is sliced in half four times on planes between opposite faces. This divides the figure into six small octahedrons at the vertexes and eight small tetrahedrons embedded in the faces:


https://www.jaapsch.net/puzzles/diamond.htm


The user manipulates the puzzle by mouse clicks on the small polyhedra thereby rotating the figure as a whole and rotating two halves of the figure wrt each other. Which means I have to do mouse picking. Xcode was giving me warnings about using depreciated OpenGL functions so I forked out the bucks for the latest editions of the OpenGL Super Bible and the OpenGL Redbook and set out to get up to speed with OpenGL Core Profile.


Neither of these references says anything about how to do mouse picking. With much head scratching and false starts I put together some code that works. I though I would post some sample code here showing how I solved the problem.



In my NSOpenGLView I catch a mouse click and initiate the pick:


// Intercept a mouse click, instigate a pick procedure

// and pass the result to client objects


-(void)mouseDown:(NSEvent *)event

{

NSPoint where;

NSString *name;

NSInteger pick_id;

NSDictionary *info;


where = [event locationInWindow];

where = [self convertPoint: where fromView: nil];


pick_id = [self pickAtPoint: where];


info = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: pick_id]

forKey: VN_PICK_FLAG];


if( [event modifierFlags] & NSEventModifierFlagOption)

name = VN_RIGHT_MOUSE_DOWN;

else

name = VN_LEFT_MOUSE_DOWN;


[[NSNotificationCenter defaultCenter] postNotificationName: name

object: self

userInfo: info];

}


-(NSUInteger)pickAtPoint: (NSPoint)where

{

NSUInteger n,

pick_id;

NSDictionary *info;

GLint x, y;

GLfloat pixel,

pixels[3][3][4];


// Load a userInfo NSDictionary with parameters (app specific)

// used by my drawing code


info = [NSDictionary dictionaryWithObjectsAndKeys:

[self modelMatrix], OGL_VIEW_MODEL_MATRIX,

[self projMatrix], OGL_VIEW_PROJ_MATRIX,

[NSNumber numberWithBool: YES ], VN_PICK_FLAG,

nil];


[[self openGLContext] makeCurrentContext];


glClearColor( 0.0 , 0.0 , 0.0 , 1.0 );

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glDisable(GL_BLEND);



// Bracket the hit point with a 3x3 pixel box

// Only these pixels will be drawn

// This is not really necessary, but it is more

// efficient to limit drawing to only those primatives

// which intersect the hit point.


x = where.x - 1;

y = where.y - 1;


glScissor( x , y , 3 , 3 );

glEnable(GL_SCISSOR_TEST);


// Notify my drawables to draw themselves using a "color"

// unique to each drawable


[[NSNotificationCenter defaultCenter] postNotificationName: OGL_VIEW_RENDER

object: self

userInfo: info];


// With a double buffered context if the buffers aren't swapped

// this rendering with weird colors never gets to the display.


glReadBuffer(GL_BACK);

glReadPixels( x , y , 3 , 3, GL_RGBA, GL_FLOAT, pixels);


glDisable(GL_SCISSOR_TEST);


//The drawable ID is encoded in the red component of the "color"

pixel = 255.0 * pixels[1][1][0];

pick_id = 0;


//Convert float to integer with no truncation error


for( n = 0 ; n < 256 ; n++ )

{

if( pixel < (float)n + 0.5)

{

pick_id = n;

break;

}

}

return pick_id;

}



My Polyhedron objects respond to the OGL_VIEW_RENDER notification:




-(void)renderWithProjMatrix: (GLKMatrix4)projMatrix

modelMatrix: (GLKMatrix4)modelMatrix

isPick: (BOOL)isPick

{

float pick_id = 0.0; // 0.0 draws the figure normally


glEnable(GL_DEPTH_TEST);

glEnable(GL_CULL_FACE);


glUseProgram([Polyhedron defaultShaderID]);


// These transforms properly place the polyhedron

// in my viewing volume.


modelMatrix = GLKMatrix4Multiply(modelMatrix, actionRotation);

modelMatrix = GLKMatrix4Multiply(modelMatrix, homeRotation);

modelMatrix = GLKMatrix4Multiply(modelMatrix, positionMatrix );


if(isPick)

{

pick_id = (home + 1.0)/ 255.0; // "home" numbers the small polyhedrons 0 thu 13

glDisable(GL_BLEND);

}


glUniformMatrix4fv(p_matrix_loc, 1, GL_FALSE, projMatrix.m);

glUniformMatrix4fv(m_matrix_loc, 1, GL_FALSE, modelMatrix.m);

glUniform1f(pick_id_loc, pick_id);


//Hook up the vertex and color data and draw the

//the 8 faces of a mini-octahedron


glBindVertexArray(vertexArrayObject_ID);


glDrawArrays(GL_TRIANGLES, 0, 8 * 3 );

}


The vertex shader keys on the pick_ID to select normal rendering or pick rendering:


// Default Polyhedron Vertex Shader


#version 410 core


uniform mat4 m_matrix;

uniform mat4 p_matrix;

uniform float pick_id;


layout (location = 0) in vec4 position;

layout (location = 1) in vec4 color;


out vec4 frag_color;


void main(void)

{

gl_Position = p_matrix * (m_matrix * position);


if( pick_id > 0.0)

frag_color = vec4( pick_id , 0.0 , 0.0 , 1.0 );

else

frag_color = color;

}


And that's it. I'm just a hack hobbyist and am just learning this stuff. Any comments would be appreciated. If I'm doing something stupid I'd like to hear about it.