0 Replies
      Latest reply on Mar 11, 2018 7:15 AM by Bruce D M
      Bruce D M Level 1 Level 1 (10 points)

        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.