Search code examples
c#openglopentk

Cannot get the correct name stack in OpenTK


I am referring to LWJGL tutorial here (https://www.youtube.com/watch?v=V9e7A4konHo) for the picking. The source code for this tutorial can be found here (https://docs.google.com/document/d/1MEcdkcCCM-BKmuL2Rg5SR-_RG6cCH7Ek9_hU7xXlW4Y/pub).

Basically what I want to do here is to translate this Java code into C# code using OpenTK. In this code, there are 2 objects (rectangles) being rendered to screen. Then I want to get the correct name stack when I click on the object. However, when I run my code, I only get the same name of 1, even though I click on other object. Also, when I click on the object, the screen get zoom in, which is not I want.

I am not sure what is wrong in my code. What I suspect the wrong part is the GluPickMatrix function. Here is my code:

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;


namespace OpenTkSelectTry
{
    public class Game2D
    {
        GameWindow window;
        private int BUFSIZE = 512;

        public Game2D(GameWindow wd)
        {
            this.window = wd;
            start();
        }

        public void start()
        {
            window.Load += loaded;
            window.Resize += resize;
            window.MouseDown += select;
            window.RenderFrame += renderFrame;      
            window.Run(1.0 / 60.0);
        }

        public void loaded(object o,EventArgs e)
        {
            GL.ClearColor(0.0f,0.0f,0.0f,0.0f);
        }

        public void renderFrame(object o,EventArgs e)
        {
            GL.LoadIdentity();
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            
            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0, 1.0, 0.0);
            GL.Vertex3(-5.0,-5.0,-80.0);
            GL.Vertex3(5.0, -5.0, -80.0);
            GL.Vertex3(5.0, 5.0, -80.0);
            GL.Vertex3(-5.0, 5.0, -80.0);
                     
            GL.Color3(1.0, 0.0, 0.0);
            GL.Vertex3(-10.0, -10.0, -80.0);
            GL.Vertex3(0.0, -10.0, -80.0);
            GL.Vertex3(0.0, 0.0, -80.0);
            GL.Vertex3(-10.0, 0.0, -80.0);
            GL.End();
            window.SwapBuffers();
        }

        public void resize(object o,EventArgs e)
        {
            GL.Viewport(0, 0, window.Width, window.Height);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-50.0, 50.0, -50.0, 50.0, -100.0, 100.0);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.Enable(EnableCap.DepthTest);
        }     

        public void select(object o, MouseEventArgs e)
        {
            var buffer = new int[BUFSIZE];
            var viewPort = new int[4];
            int hits;
            GL.GetInteger(GetPName.Viewport, viewPort);
            GL.SelectBuffer(BUFSIZE, buffer);
            GL.RenderMode(RenderingMode.Select);

            GL.InitNames();
            GL.PushName(0);
            GL.MatrixMode(MatrixMode.Projection);
            GL.PushMatrix();
            GL.LoadIdentity();

            GluPickMatrix(e.Mouse.X, viewPort[3] - e.Mouse.Y, 5.0, 5.0, viewPort);  
            GL.Ortho(-50.0, 50.0, -50.0, 50.0, -100.0, 100.0);             
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.LoadName(1);
            GL.Begin(BeginMode.Quads);
            GL.Color3(1.0, 1.0, 0.0);
            GL.Vertex3(-5.0, -5.0, -80.0);
            GL.Vertex3(5.0, -5.0, -80.0);
            GL.Vertex3(5.0, 5.0, -80.0);
            GL.Vertex3(-5.0, 5.0, -80.0);
   
            GL.LoadName(2);
            GL.Color3(1.0, 0.0, 0.0);
            GL.Vertex3(-10.0, -10.0, -80.0);
            GL.Vertex3(0.0, -10.0, -80.0);
            GL.Vertex3(0.0, 0.0, -80.0);
            GL.Vertex3(-10.0, 0.0, -80.0);
            GL.End();    
            GL.PopMatrix();

            GL.Flush();

            hits = GL.RenderMode(RenderingMode.Render);
            Console.WriteLine("hit: {0}", hits);

            if (hits>0)
            {
                int choose = buffer[3];
                int depth = buffer[1];
                for (int i=0;i<hits;i++)
                {
                    if (buffer[i * 4 + 1] < depth)
                    {
                        choose = buffer[i * 4 + 3];
                        depth = buffer[i * 4 + 1];
                    }
                }
                Console.WriteLine("choosen: {0}", choose);
            }     
        }
        private void GluPickMatrix(double x, double y, double deltax, double deltay, int[] viewport)
        {
            if (deltax <= 0 || deltay <= 0)
            {
                return;
            }

            GL.Translate((viewport[2] - 2 * (x - viewport[0])) / deltax, (viewport[3] - 2 * (y - viewport[1])) / deltay, 0);
            GL.Scale(viewport[2] / deltax, viewport[3] / deltay, 1.0);
        }
    }
}

The main function:

using System;
using OpenTK;

namespace OpenTkSelectTry
{
    class Program
    {
        static void Main(string[] args)
        {
            var window = new GameWindow(500, 500);
            var gm = new Game2D(window);
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press enter to finish...");
            Console.ReadLine();
        }
    }
}

Here is the 2 objects rendered in the screen initially:

Initial objects rendered on screen

After I click on the object, the screen zoom in (not what I want), and the name stack is not correct (always 1).

The screen get zoom in(not what I want)


Solution

  • the screen zoom in (not what I want),

    This is because you've changed the matrix mode in the select method. Choose the MatrixMode.Projection, before GL.PopMatrix():

    and the name stack is not correct (always 1).

    It is not allowed to call GL.LoadName within a GL.Begin/GL.End sequence. This causes an invalide operation error (GL.GetError). You need to draw 2 separate quads.

    public void select(object o, MouseEventArgs e)
    {
        // [...]
    
        GL.MatrixMode(MatrixMode.Projection);
        GL.PushMatrix();
        GL.LoadIdentity();
    
        GluPickMatrix(e.Mouse.X, viewPort[3] - e.Mouse.Y, 5.0, 5.0, viewPort);
        GL.Ortho(-50.0, 50.0, -50.0, 50.0, -100.0, 100.0);
    
        GL.MatrixMode(MatrixMode.Modelview);
        GL.LoadIdentity();
    
        GL.LoadName(1);
        GL.Begin(BeginMode.Quads);
        GL.Color3(1.0, 1.0, 0.0);
        GL.Vertex3(-5.0, -5.0, -80.0);
        GL.Vertex3(5.0, -5.0, -80.0);
        GL.Vertex3(5.0, 5.0, -80.0);
        GL.Vertex3(-5.0, 5.0, -80.0);
        GL.End();
    
        GL.LoadName(2);
        GL.Begin(BeginMode.Quads);
        GL.Color3(1.0, 0.0, 0.0);
        GL.Vertex3(-10.0, -10.0, -80.0);
        GL.Vertex3(0.0, -10.0, -80.0);
        GL.Vertex3(0.0, 0.0, -80.0);
        GL.Vertex3(-10.0, 0.0, -80.0);
        GL.End();
      
        GL.MatrixMode(MatrixMode.Projection);
        GL.PopMatrix();
        GL.MatrixMode(MatrixMode.Modelview);
    
        // [...]
    }
    


    The original example uses Perspective projection instead of Orthographic projection. Use Matrix4.CreatePerspectiveFieldOfView to specify a perspective projection matrix. Use GL.MultMatrix to multiply the current matrix by the projection matrix:

    GL.Ortho(-50.0, 50.0, -50.0, 50.0, -100.0, 100.0);

    Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(
        40f * (float)Math.PI / 180f, 500f / 500f, 0.001f, 400f);
    GL.MultMatrix(ref perspective);