Samssonart
Samssonart

Reputation: 3593

How do I make opengl objects appear on mouse click?

What I need to do is click somewhere on my OpenGL viewport and have an object render in that spot. I already know the basic stuff, i.e. creating the window, detecting mouse clicks, getting the click coordinates and draw objects. The missing link I have is getting the coordinates right. I need to determine what the (X,Y) point I click on the screen means openGL world coordinates. I am aware of a GLUT function called gluUnProject, but due to technical reasons I can't use GLUT. I've checked many algorithms to make my own unproject function by hand:

http://schabby.de/picking-opengl-ray-tracing/ http://collagefactory.blogspot.mx/2010/03/gluunproject-source-code.html OpenGL Math - Projecting Screen space to World space coords

And others, but none worked, when I clicked on the screen the object was drawn in weird places. I'm not even sure what I'm looking for here, I don't know if what I'm trying to implement is Picking, or Raycasting or Raypicking or something else. Does anyone know the algorithm I'm looking for?

EDIT: I'm adding a screen capture, I marked the spot where I clicked and, as you can see, the sphere is drawing in a different place. http://img23.imageshack.us/img23/4738/proofxd.jpg I'm also adding my opengl source code:

    /**OPENGL**/
    private void GLC_display_Load(object sender, EventArgs e)
    {
        //CONTEXT
        GL.ClearColor(Color.LightGray);
        GL.Enable(EnableCap.Texture2D);
        GL.Enable(EnableCap.Blend);
        GL.Enable(EnableCap.DepthTest);
        GL.Enable(EnableCap.CullFace);
        GL.CullFace(CullFaceMode.Back);
        GL.Enable(EnableCap.Blend);
        GL.DepthFunc(DepthFunction.Always);     
        GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
        GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest); 
        SetupViewport();
    }


    private void SetupViewport()
    {
        GL.Clear(ClearBufferMask.ColorBufferBit);
        GL.ShadeModel(ShadingModel.Smooth);
        float aspectRatio = (float)GLC_display.Width / (float)GLC_display.Height;
        GL.Viewport(0, 0, GLC_display.Width, GLC_display.Height);
        GL.MatrixMode(MatrixMode.Projection);
        GL.LoadIdentity();
        OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView((float)(System.Math.PI / 4f), aspectRatio, 0.1f, 2000f);
        GL.MultMatrix(ref perspective);
        GL.MatrixMode(MatrixMode.Modelview);
        GL.LoadIdentity();
    }

    private void GLC_display_Resize(object sender, EventArgs e)
    {
        SetupViewport();
        GLC_display.Invalidate();
    }

    private void GLC_display_Paint(object sender, EventArgs e)
    {

        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        Matrix4 lookat = Matrix4.LookAt(posVector, eyeVector, Vector3.UnitY);
        GL.MatrixMode(MatrixMode.Modelview);
        GL.LoadMatrix(ref lookat);
        GL.Color3(Color.Red);
        //GL.PushMatrix();
        //GL.Translate(new Vector3(10f,3f,5f));
        //Geometry.drawSphere(5, 20);
        //GL.PopMatrix();

        foreach (Vector3 coord in clicks)
        {
            Console.WriteLine("List: "+coord.ToString());
            //GL.Color3(0f, 1f, 0f);
            GL.PushMatrix();
            GL.Translate(coord);
            Geometry.drawSphere(5, 20);
            GL.PopMatrix();
        }
        GLC_display.SwapBuffers();
    }

    public Vector3 unProjectInput(Vector2 mouse)
    {
        Vector4 result;
        int[] view = new int[4];
        GL.GetInteger(GetPName.Viewport, view);

        mouse.Y = view[3] - mouse.Y;
        result.X = (2f*((float)(mouse.X - view[0])/view[2])) - 1f;
        result.Y = (2f*((float)(mouse.Y - view[1]) / view[3])) - 1f;
        result.Z = 0f;
        result.W = 1f;

        double[] projection = new double[16];
        GL.GetDouble(GetPName.ProjectionMatrix, projection);
        Matrix4 Projection = new Matrix4((float)projection[0], (float)projection[1], (float)projection[2], (float)projection[3],
                                                   (float)projection[4], (float)projection[5], (float)projection[6], (float)projection[7],
                                                   (float)projection[8], (float)projection[9], (float)projection[10], (float)projection[11],
                                                   (float)projection[12], (float)projection[13], (float)projection[14], (float)projection[15]);
        double[] modelViewMatrix = new double[16];
        GL.GetDouble(GetPName.ModelviewMatrix, modelViewMatrix);
        Matrix4 MV = new Matrix4((float)modelViewMatrix[0], (float)modelViewMatrix[1], (float)modelViewMatrix[2], (float)modelViewMatrix[3],
                                                   (float)modelViewMatrix[4], (float)modelViewMatrix[5], (float)modelViewMatrix[6], (float)modelViewMatrix[7],
                                                   (float)modelViewMatrix[8], (float)modelViewMatrix[9], (float)modelViewMatrix[10], (float)modelViewMatrix[11],
                                                   (float)modelViewMatrix[12], (float)modelViewMatrix[13], (float)modelViewMatrix[14], (float)modelViewMatrix[15]);

        Matrix4 iMVP = Matrix4.Invert(Matrix4.Mult(MV, Projection));
        result = Vector4.Transform(result, iMVP);

        if (result.W > float.Epsilon || result.W < float.Epsilon)
        {
            result.X /= result.W;
            result.Y /= result.W;
            result.Z = 0f;
            //result.Z /= result.W;
        }

        return result.Xyz;
    }

Upvotes: 0

Views: 2188

Answers (2)

zacaj
zacaj

Reputation: 2065

vec3f cameraPos=camera->position();
vec3f objectPos=vec3f(
    (float)(mousePos.x)/(videoProperties.w)*2-1,
    1,
    (float)(((videoProperties.h)-mousePos.y))/(videoProperties.h)*2-1);
objectPos.x/=(videoProperties.h)/(videoProperties.w);
objectPos*=distance;
objectPos+=cameraPos;
objectPos*=camera->transformation();//may need to inverse this depending on your camera system

This is what I use in my engine

Upvotes: 0

ravuya
ravuya

Reputation: 8756

GLU and GLUT aren't the same thing. GLU functionality should be present on any reasonably modern OpenGL installation.

For what it's worth, the implementation of gluUnProject is actually spelled out in its man page.

Upvotes: 1

Related Questions