I am working on an OpenGL project which requires object selection feature. I use OpenTK framework to do this; however OpenTK doesn't support glu.PickMatrix() method to define the picking region. I ended up googling its implementation and here is what i got:
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);
}
I totally fail to understand this piece of code. Moreover, this doesn't work with my following code sample:
//selectbuffer
private int[] _selectBuffer = new int[512];
private void Init()
{
float[] triangleVertices = new float[] { 0.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f };
float[] _triangleColors = new float[] { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
GL.GenBuffers(2, _vBO);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[0]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * _triangleVertices.Length), _triangleVertices, BufferUsageHint.StaticDraw);
GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[1]);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(sizeof(float) * _triangleColors.Length), _triangleColors, BufferUsageHint.StaticDraw);
GL.ColorPointer(3, ColorPointerType.Float, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.ColorArray);
//Selectbuffer set up
GL.SelectBuffer(512, _selectBuffer);
}
private void glControlWindow_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Clear(ClearBufferMask.DepthBufferBit);
float[] eyes = { 0.0f, 0.0f, -10.0f };
float[] target = { 0.0f, 0.0f, 0.0f };
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(0.785398163f, 4.0f / 3.0f, 0.1f, 100f); //45 degree = 0.785398163 rads
Matrix4 view = Matrix4.LookAt(eyes[0], eyes[1], eyes[2], target[0], target[1], target[2], 0, 1, 0);
Matrix4 model = Matrix4.Identity;
Matrix4 MV = view * model;
//First Clear Buffers
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Clear(ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.LoadMatrix(ref MV);
GL.Viewport(0, 0, glControlWindow.Width, glControlWindow.Height);
GL.Enable(EnableCap.DepthTest); //Enable correct Z Drawings
GL.DepthFunc(DepthFunction.Less); //Enable correct Z Drawings
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.Translate(3.0f, 0.0f, 0.0f);
DrawTriangle();
GL.PopMatrix();
GL.PushMatrix();
GL.Translate(-3.0f, 0.0f, 0.0f);
DrawTriangle();
GL.PopMatrix();
//Finally...
GraphicsContext.CurrentContext.VSync = true; //Caps frame rate as to not over run GPU
glControlWindow.SwapBuffers(); //Takes from the 'GL' and puts into control
}
private void DrawTriangle()
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO[0]);
GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
GL.EnableClientState(ArrayCap.VertexArray);
GL.DrawArrays(BeginMode.Triangles, 0, 3);
GL.DisableClientState(ArrayCap.VertexArray);
}
//mouse click event implementation
private void glControlWindow_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
//Enter Select mode. Pretend drawing.
GL.RenderMode(RenderingMode.Select);
int[] viewport = new int[4];
GL.GetInteger(GetPName.Viewport, viewport);
GL.PushMatrix();
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GluPickMatrix(e.X, e.Y, 5, 5, viewport);
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(0.785398163f, 4.0f / 3.0f, 0.1f, 100f); // this projection matrix is the same as one in glControlWindow_Paint method.
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
int i = 0;
int hits;
GL.PushMatrix();
GL.Translate(3.0f, 0.0f, 0.0f);
GL.PushName(i);
DrawTriangle();
GL.PopName();
GL.PopMatrix();
i++;
GL.PushMatrix();
GL.Translate(-3.0f, 0.0f, 0.0f);
GL.PushName(i);
DrawTriangle();
GL.PopName();
GL.PopMatrix();
hits = GL.RenderMode(RenderingMode.Render);
.....hits processing code goes here...
GL.PopMatrix();
glControlWindow.Invalidate();
}
I expect to get only one hit everytime i click inside a triangle, but i always get 2 no matter where i click. I suspect there is something wrong with the implementation of the GluPickMatrix, I haven't figured out yet.