darkprinze's picture

GLControl zoomin, zoomout and rotate using mouse

I am successfully rendered the stl file and display the points in the cloud. I am trying to implement the smooth zooming function using mouse. Currently i am modifying FOV value to zoom in but its very slow and laggy. How to implement the smooth zooming and rotation like google earth.

 var projection = Matrix4.CreatePerspectiveFieldOfView(viewAngle * zoomFactor, float.Parse(aspect_ratio.ToString()),
                                                          nearClip, farClip);

In mouse wheel event i am changing value.

 private void glSurface_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Delta > 0)
            {
                zoomFactor += 0.1f;
            }
            else
            {
                zoomFactor -= 0.1f;
            }            
        }

Is it better to modify Lookat function for zooming or i have to use scale function? It would be great if i have some sample code or point me in right direction.

Also, i am using immediate mode. which is better? immediate mode or VBO?

Inline Images
Model

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Since you have a perspective projection, you should modify your modelview matrix to implement zoom. Imagine your camera in 3d space moving closer to the earth.

VBOs are strictly better than immediate mode and can perform orders of magnitude faster. Immediate mode is only useful for quick prototyping (and even in that case, setting up a VBO is just a few lines of code.)

darkprinze's picture

Thanks. I modified the code and now i am modifying the model view lookat function for zooming. In the lookat function i am modifying z eye co ordinates to zoom. However, its working smoothly for small stl files and little laggy and acting weird when deep zoomed(kinda model is rotating) in for big stl files. Also, rotation is weird for foot model, and instead of rotating around center its rotating around origin. My modified code is below.

The lag is appear only for big stl files which has 7242 vertices and 14150 faces. I am also having difficulty to set the camera to center of the screen

 var modelview = Matrix4.LookAt(0f, 20f, zoomFactor, 0, 0, 0, 0.0f, 1.0f, 0.0f);
            var aspect_ratio = Width / (float)Height;
            var projection = Matrix4.CreatePerspectiveFieldOfView(viewAngle, float.Parse(aspect_ratio.ToString()),
                                                          nearClip, farClip);
 
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projection);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref modelview);
            GL.Rotate(angleY, 1.0f, 0, 0);
            GL.Rotate(angleX, 0, 1.0f, 0);
            //GL.Translate(-boxOffset);

Mouse coordinates

 private void glControl_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {            
            System.Windows.Forms.MouseEventArgs ev = (e as System.Windows.Forms.MouseEventArgs);
            _mouseStartX = ev.X;
            _mouseStartY = ev.Y;          
        }
 
        private void glControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
 
        }
 
        private void glControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            System.Windows.Forms.MouseEventArgs ev = (e as System.Windows.Forms.MouseEventArgs);
            if (ev.Button == MouseButtons.Left)
            {               
                angleX += (e.X - _mouseStartX);
                angleY -= (e.Y - _mouseStartY);
               glControl.Invalidate();
            }         
            _mouseStartX = ev.X;
            _mouseStartY = ev.Y;            
        }
 
        private void glControl_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {           
            if (e.Delta > 0)
            {
                zoomFactor += 7f;
            }
            else
            {
                zoomFactor -= 7f;
            }           
            glControl.Invalidate();
        }
darkprinze's picture

I am still try to figure out how to implement the proper zoom. These are the following things i am trying to implementation.

1. Fit the 3D model into the screen
2. Smooth zoom and rotate (rotate arounf center point) function (as of now Its lag and slow).

What i am trying to do is create the similar UI like Meshlab (Only zoom and rotate function). I have the size and center position of model.

I starting to grasp how Lookat function and rendering works, but i am having difficulty to implement them properly. I have attached my output screenshot.

Is there any example available for reference?

Amir's picture

Hi,

My solution to Zoom, Pan and Camera Orbit is for windows form.
Please,
1- create a form and name it "Main_Form",
2- create a glControl and name it "glControl",
3- Create a label and name it "Frame_label",
4- Copy and paste the following code into it.
5- Assign events to your "Main_Form" and "glControl" manually.

The functions and their corresponding mouse buttons are:
Zoom: mouse wheel, Pan: Left click, Rotate: Right click.

The main part for a real pan in my solution is "GL.Viewport" in "glControl_MouseMove".

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
 
namespace PointCloudForm
{
    public partial class Main_Form : Form
    {
        int vbo_id;
        int vbo_size;
        Matrix4 modelview, projection;
        uint frameNum;
 
        const int CloudSize = 48;
        const float pointSize = 0.01f;
        const bool HighQuality = true;
        public Main_Form()
        {
            InitializeComponent();
        }
 
        private void Main_Form_Load(object sender, EventArgs e)
        {
 
        }
 
        private void Main_Form_Resize(object sender, EventArgs e)
        {
            SetupViewport();
            glControl.Invalidate();
        }
 
        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MouseMove += new MouseEventHandler(glControl_MouseMove);
            glControl.MouseWheel += new MouseEventHandler(glControl_MouseWheel);
 
            GL.ClearColor(Color.DarkSlateGray); // Yey! .NET Colors can be used directly!
            GL.PointSize(pointSize);
            GL.Color3(1f, 1f, 1f); // Points Color
            SetupViewport();
        }
 
        private void SetupViewport()
        {
            if (this.WindowState == FormWindowState.Minimized) return;
            glControl.Width = this.Width - 32;
            glControl.Height = this.Height - 80;
            Frame_label.Location = new Point(glControl.Width / 2, glControl.Height + 25);
            GL.MatrixMode(MatrixMode.Projection);
            //GL.LoadIdentity();
            GL.Ortho(0, glControl.Width, 0, glControl.Height, -1, 1); // Bottom-left corner pixel has coordinate (0, 0)
            GL.Viewport(0, 0, glControl.Width, glControl.Height); // Use all of the glControl painting area
            GL.Enable(EnableCap.DepthTest);
 
            // Improve visual quality at the expense of performance
            if (HighQuality)
            {
                int max_size;
                GL.GetInteger(GetPName.PointSizeMax, out max_size);
                GL.Enable(EnableCap.PointSmooth);
            }
 
            // Imagine that the cloud is a bool[CloudSize, CloudSize, CloudSize] array.
            // This code translates the point cloud into vertex coordinates
            var vertices = new Vector3[CloudSize * CloudSize * CloudSize];
            int index = 0;
            for (int i = 0; i < CloudSize; i++)
                for (int j = 0; j < CloudSize; j++)
                    for (int k = 0; k < CloudSize; k++)
                        if (Math.Sqrt(i * i + j * j + k * k) < CloudSize) // Point cloud shaped like a sphere
                        {
                            vertices[index++] = new Vector3(
                                -CloudSize / 2 + i,
                                -CloudSize / 2 + j,
                                -CloudSize / 2 + k);
                        }
 
            // Load those vertex coordinates into a VBO
            vbo_size = vertices.Length; // Necessary for rendering later on
            GL.GenBuffers(1, out vbo_id);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_id);
            GL.BufferData(BufferTarget.ArrayBuffer,
                          new IntPtr(vertices.Length * BlittableValueType.StrideOf(vertices)),
                          vertices, BufferUsageHint.StaticDraw);
 
            float aspect_ratio = this.Width / (float)this.Height;
            projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect_ratio, 1, 1024);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projection);
        }
 
        private void glControl_Paint(object sender, PaintEventArgs e)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit |
                     ClearBufferMask.DepthBufferBit |
                     ClearBufferMask.StencilBufferBit);
 
            if (HighQuality)
            {
                GL.PointParameter(PointParameterName.PointDistanceAttenuation,
                    new float[] { 0, 0, (float)Math.Pow(1 / (projection.M11 * Width / 2), 2) });
            }
 
            modelview = Matrix4.LookAt(0f, 20f, -200f + zoomFactor, 0, 0, 0, 0.0f, 1.0f, 0.0f);
            var aspect_ratio = Width / (float)Height;
            projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver6, aspect_ratio, 1, 512);
 
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projection);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref modelview);
            GL.Rotate(angleY, 1.0f, 0, 0);
            GL.Rotate(angleX, 0, 1.0f, 0);
            //GL.Translate(panX, panY, 0f);
 
            // To draw a VBO:
            // 1) Ensure that the VertexArray client state is enabled.
            // 2) Bind the vertex and element buffer handles.
            // 3) Set up the data pointers (vertex, normal, color) according to your vertex format.
 
            GL.EnableClientState(ArrayCap.VertexArray);
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_id);
            GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, new IntPtr(0));
            GL.DrawArrays(PrimitiveType.Points, 0, vbo_size);
 
            glControl.SwapBuffers();
 
            Frame_label.Text = "Frame: " + frameNum++;
        }
 
        #region GLControl. Mouse event handlers
        private int _mouseStartX = 0;
        private int _mouseStartY = 0;
        private float angleX = 0;
        private float angleY = 0;
        private float panX = 0;
        private float panY = 0;
 
        private void glControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                angleX += (e.X - _mouseStartX);
                angleY -= (e.Y - _mouseStartY);
 
                this.Cursor = Cursors.Cross;
 
                glControl.Invalidate();
            }
            if (e.Button == MouseButtons.Left)
            {
                panX += (e.X - _mouseStartX);
                panY -= (e.Y - _mouseStartY);
                GL.Viewport((int)panX, (int)panY, glControl.Width, glControl.Height); // Use all of the glControl painting area
                this.Cursor = Cursors.Hand;
                glControl.Invalidate();
            }
            _mouseStartX = e.X;
            _mouseStartY = e.Y;
        }
 
        float zoomFactor;
        private void glControl_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0) zoomFactor += 7f;
            else zoomFactor -= 7f;
            glControl.Invalidate();
        }
        #endregion        
 
        private void glControl_MouseUp(object sender, MouseEventArgs e)
        {
            this.Cursor = Cursors.Default;
        }
    }
}

PointCloudViewer