silentcoder89's picture

How to use Matrix4.LookAt()?[NEWBIE]

Hi,

I wanted to create a multi-view 3D application. So far i have come up as in the following picture (i'm testing using a cube)

Uploaded with ImageShack.us

Currently, i did not use the Matrix4.LookAt() function to set the "rotation" of each viewport


I wanted to use the Matrix4.LookAt() function instead of GL.Rotate(). But im not sure how to use it. Can someone give me a guide?

If you want to view my code, refer the following:

MainForm.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using Loader;
 
namespace MyOpenTK
{
	public partial class MainForm : Form
	{
		// Variables
		private bool loaded = false;
		private bool selectMode = false;
		private bool rotateMode = false;
		private bool dragging = false;
		private struct Mesh {
			public Vector3 Position, Normal, Rotation;
			public Mesh(Vector3 Position, Vector3 Normal, Vector3 Rotation){
				this.Position = Position;
				this.Normal = Normal;
				this.Rotation = Rotation;
			}
		}
		private Mesh ViewPort = new Mesh(new Vector3(0.0f,0.0f,5.0f), new Vector3(0.0f,0.0f,0.0f), new Vector3(0.0f,0.0f,0.0f));
 
		// [OPTIONAL]
		private Vector3 eye = new Vector3(0.0f,0.0f,0.0f);
		private Vector3 cent = new Vector3(0.0f,0.0f,1.0f);
		// End Variables
 
		// Form Specific		
		public MainForm(){ InitializeComponent(); }
		// End Form Specific
 
		// OpenTK Display Specific
		public void DisplayLoad(Object sender, EventArgs e){
			loaded = true;
			GL.ClearColor(Color.Black);
			SetupViewport((GLControl)sender);
		}
		public void DisplayResize(Object sender, EventArgs e){
			if(!loaded){ return; }
			SetupViewport(glControl1);
			SetupViewport(DisplayFront);
			SetupViewport(DisplaySide);
			SetupViewport(DisplayTop);
			glControl1.Invalidate();
		}
		public void DisplayMouseDown(Object sender,MouseEventArgs e){
			if(!loaded){ return; }
			dragging = true;
			lastMouse = e.Location;
		}
		Point lastMouse;float delta;
		public void DisplayMouseMove(Object sender,MouseEventArgs e){
			if(!loaded){ return; }
			if(dragging){
				// do dragging events here
				if(e.Button == MouseButtons.Left){
					if(selectMode){
						if(e.X > lastMouse.X){
							delta = Math.Abs(e.Location.X - lastMouse.X);
							//delta = (delta > 1) ? delta : 0;
							ViewPort.Position.X -= (float)delta/100;
						}else{
							ViewPort.Position.X += (float)delta/100;
						}
						if(e.Y > lastMouse.Y){
							delta = Math.Abs(e.Location.Y - lastMouse.Y);
							//delta = (delta > 1) ? delta : 0;
							ViewPort.Position.Y -= (float)delta/100;
						}else{
							ViewPort.Position.Y += (float)delta/100;
						}
						glControl1.Invalidate();
						lastMouse = e.Location;
					}
					if(rotateMode){
						if(e.X > lastMouse.X){
							ViewPort.Rotation.X -= 1.0f;
						}else{
							ViewPort.Rotation.X += 1.0f;
						}
						if(e.Y > lastMouse.Y){
							ViewPort.Rotation.Y -= 1.0f;
						}else{
							ViewPort.Rotation.Y += 1.0f;
						}
						glControl1.Invalidate();
						lastMouse = e.Location;
					}
				}else if(e.Button == MouseButtons.Middle){
					if(selectMode){
						if(e.Y > lastMouse.Y){
							delta = e.Y - lastMouse.Y;
							ViewPort.Position.Z -= 0.1f;
						}else{
							delta = e.Y - lastMouse.Y;
							ViewPort.Position.Z += 0.1f;
						}
						glControl1.Invalidate();
						lastMouse = e.Location;
					}
				}
				ViewportPos.Text = "{x:"+ViewPort.Position.X+",y:"+ViewPort.Position.Y+",z:"+ViewPort.Position.Z+
									", rotX:"+ViewPort.Rotation.X+",rotY:"+ViewPort.Rotation.Y+",rotZ:"+ViewPort.Rotation.Z+"}";
			}else{
				// mouse positioning info here
			}
		}
		public void DisplayMouseUp(Object sender,MouseEventArgs e){
			if(!loaded){ return; }
			dragging = false;
		}
		public void DisplayPaint(Object sender, EventArgs e){
			if (!loaded) return;
 
			glControl1.MakeCurrent();
 
 			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
 			Matrix4 modelview = Matrix4.LookAt(new Vector3(0f,0.0f,-1f), Vector3.Zero, Vector3.UnitY);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref modelview);
 
            GL.Enable(EnableCap.DepthTest);
 
		    GL.Translate(ViewPort.Position);
            GL.Rotate(ViewPort.Rotation.X, Vector3.UnitX);
            GL.Rotate(ViewPort.Rotation.Y, Vector3.UnitY);
            GL.Rotate(ViewPort.Rotation.Z, Vector3.UnitZ);
 
            Render();
 
      		glControl1.SwapBuffers();
 
      		DisplayFront.Invalidate();
      		DisplaySide.Invalidate();
      		DisplayTop.Invalidate();
		}
 
		private void Render(){
			GL.Begin(BeginMode.Quads);
				// Left
				GL.Color3(Color.Red);
			    GL.Vertex3(1.0f,1.0f,1.0f);
			    GL.Vertex3(1.0f,-1.0f,1.0f);
			    GL.Vertex3(1.0f,-1.0f,-1.0f);
			    GL.Vertex3(1.0f,1.0f,-1.0f);
			    // Right
			    GL.Color3(Color.Red);
			    GL.Vertex3(-1.0f,1.0f,1.0f);
			    GL.Vertex3(-1.0f,-1.0f,1.0f);
			    GL.Vertex3(-1.0f,-1.0f,-1.0f);
			    GL.Vertex3(-1.0f,1.0f,-1.0f);
			    // Top
			    GL.Color3(Color.LightGreen);
			    GL.Vertex3(1.0f,1.0f,1.0f);
			    GL.Vertex3(-1.0f,1.0f,1.0f);
			    GL.Vertex3(-1.0f,1.0f,-1.0f);
			    GL.Vertex3(1.0f,1.0f,-1.0f);
			    // Bottom
			    GL.Color3(Color.LightGreen);
			    GL.Vertex3(1.0f,-1.0f,1.0f);
			    GL.Vertex3(-1.0f,-1.0f,1.0f);
			    GL.Vertex3(-1.0f,-1.0f,-1.0f);
			    GL.Vertex3(1.0f,-1.0f,-1.0f);
			    // Back
			    GL.Color3(Color.Blue);
			    GL.Vertex3(1.0f,1.0f,1.0f);
			    GL.Vertex3(-1.0f,1.0f,1.0f);
			    GL.Vertex3(-1.0f,-1.0f,1.0f);
			    GL.Vertex3(1.0f,-1.0f,1.0f);
			    // Front
			    GL.Color3(Color.Blue);
			    GL.Vertex3(1.0f,1.0f,-1.0f);
			    GL.Vertex3(-1.0f,1.0f,-1.0f);
			    GL.Vertex3(-1.0f,-1.0f,-1.0f);
			    GL.Vertex3(1.0f,-1.0f,-1.0f);
            GL.End();
		}
 
		private void SetupViewport(GLControl Display)
	    {
			Display.MakeCurrent();
			GL.Viewport(Display.ClientRectangle.X, Display.ClientRectangle.Y, 
			            Display.ClientRectangle.Width, Display.ClientRectangle.Height);
 
            Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Display.Width / (float)Display.Height, 1.0f, 64.0f);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projection);
	    }
		// End OpenTK Display Specific
 
		// Secondary Display
		void DisplaySecondaryLoad(object sender, EventArgs e)
		{
			SetupViewport((GLControl)sender);
		}
		void DisplayFrontPaint(object sender, PaintEventArgs e)
		{
			if (!loaded) return;
 
			DisplayFront.MakeCurrent();
 
 			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
		    Matrix4 modelview = Matrix4.LookAt(Vector3.Zero, Vector3.UnitZ, Vector3.UnitY);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref modelview);
 
            GL.Enable(EnableCap.DepthTest);
 
		    GL.Translate(ViewPort.Position);
            GL.Rotate(ViewPort.Rotation.X, Vector3.UnitX);
            GL.Rotate(ViewPort.Rotation.Y, Vector3.UnitY);
            GL.Rotate(ViewPort.Rotation.Z, Vector3.UnitZ);
 
            Render();
 
      		DisplayFront.SwapBuffers();
		}
		void DisplaySidePaint(object sender, PaintEventArgs e)
		{
			if (!loaded) return;
 
			DisplaySide.MakeCurrent();
 
 			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
 			Matrix4 sidemodelview = Matrix4.LookAt(Vector3.Zero,Vector3.UnitZ, Vector3.UnitY);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref sidemodelview);
 
            GL.Enable(EnableCap.DepthTest);
 
		    GL.Translate(ViewPort.Position);
            GL.Rotate(ViewPort.Rotation.X, Vector3.UnitX);
            GL.Rotate(ViewPort.Rotation.Y + 90, Vector3.UnitY);
            GL.Rotate(ViewPort.Rotation.Z, Vector3.UnitZ);
 
            Render();
 
      		DisplaySide.SwapBuffers();
		}
		void DisplayTopPaint(object sender, PaintEventArgs e)
		{
			if (!loaded) return;
 
			DisplayTop.MakeCurrent();
 
 			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
		    Matrix4 topmodelview = Matrix4.LookAt(Vector3.Zero,Vector3.UnitZ, Vector3.UnitY);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref topmodelview);
 
            GL.Enable(EnableCap.DepthTest);
 
		    GL.Translate(ViewPort.Position);
            GL.Rotate(ViewPort.Rotation.X - 90, Vector3.UnitX);
            GL.Rotate(ViewPort.Rotation.Y, Vector3.UnitY);
            GL.Rotate(ViewPort.Rotation.Z, Vector3.UnitZ);
 
            Render();
 
      		DisplayTop.SwapBuffers();
		}
		// End Secondary Display
 
		void ToolStripButton5Click(object sender, EventArgs e)
		{
			if(toolStripButton5.BackColor != Color.Gray){
				glControl1.Cursor = Cursors.Hand;
				toolStripButton5.BackColor = Color.Gray;
				selectMode = true;
			}else{
				glControl1.Cursor = Cursors.Default;
				toolStripButton5.BackColor = Color.Transparent;
				selectMode = false;
			}
		}
 
		void ToolStripButton6Click(object sender, EventArgs e)
		{
			if(toolStripButton6.BackColor != Color.Gray){
				glControl1.Cursor = Cursors.Hand;
				toolStripButton6.BackColor = Color.Gray;
				rotateMode = true;
			}else{
				glControl1.Cursor = Cursors.Default;
				toolStripButton6.BackColor = Color.Transparent;
				rotateMode = false;
			}
		}
 
		void DSMax3dsToolStripMenuItemClick(object sender, EventArgs e)
		{
			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Filter = "3ds File|*.3ds|All Files|*.*";
			if(ofd.ShowDialog() == DialogResult.OK){
				Loader.Loader ldr = new Loader.Loader();
				if(ldr.Open(ofd.FileName)){
					ldr.parse3DS();
					ldr.Close();
				}
			}
		}
 
		void ExitToolStripMenuItemClick(object sender, EventArgs e)
		{
			Application.Exit();
		}
 
		void MainFormLoad(object sender, EventArgs e)
		{
			glControl1.MakeCurrent();
			DataGridViewRow item = new DataGridViewRow();
			DataGridViewCell c1 = new DataGridViewTextBoxCell();
			c1.Value = "Cube";
			DataGridViewCell c2 = new DataGridViewTextBoxCell();
			c2.Value = "●";
			item.Cells.Add(c1);
			item.Cells.Add(c2);
			dataGridView1.Rows.Add(item);
		}
 
		void MainFormResize(object sender, EventArgs e)
		{
 
		}
 
		void ToolStripButton8Click(object sender, EventArgs e)
		{
			if(dataGridView1.SelectedCells.Count == 0) 
				MessageBox.Show("Please select a frame");
			else{
				foreach(DataGridViewCell cell in dataGridView1.SelectedCells){
					dataGridView1.Columns.RemoveAt(cell.ColumnIndex);
				}
			}
		}
 
		void ToolStripButton7Click(object sender, EventArgs e)
		{
			if(dataGridView1.Rows.Count==0){
				MessageBox.Show("Please add a mesh first");
			}else{
				DataGridViewTextBoxColumn frame = new DataGridViewTextBoxColumn();
				frame.Name = "Frame"+(dataGridView1.Columns.Count);
				frame.HeaderText = (dataGridView1.Columns.Count).ToString();
				frame.Width = 20;
				dataGridView1.Columns.Add(frame);
			}
		}
 
		void NumericUpDownValueChanged(object sender, EventArgs e)
		{
			eye.X = (float)eyeX.Value;
			eye.Y = (float)eyeY.Value;
			eye.Z = (float)eyeZ.Value;
			cent.X = (float)centX.Value;
			cent.Y = (float)centY.Value;
			cent.Z = (float)centZ.Value;
			SetupViewport(DisplaySide);
			DisplaySide.Invalidate();
		}
	}
}

MyClass.cs

/*
 * Created by SharpDevelop.
 * User: Haziman
 * Date: 15/07/2010
 * Time: 19:30
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using OpenTK;
using Loader.Types;
 
namespace Loader
{
	namespace Types {
		public struct Vertex {
			public Vector3 Position;
			public Vertex(Vector3 Position){
				this.Position = Position;
			}
		}
		public struct Normal {
			public Vector3 Position;
			public Normal(Vector3 Position){
				this.Position = Position;
			}
		}
		public struct Texture {
			public float U, V, W;
			public Texture(float U, float V, float W){
				this.U = U;
				this.V = V;
				this.W = W;
			}
		}
		public struct Face {
			public uint[] iv, ivn, ivt;
			public Material mat;
			public Face(uint[] iv, uint[] ivn, uint[] ivt, Material mat){
				this.iv = iv;
				this.ivn = ivn;
				this.ivt = ivt;
				this.mat = mat;
			}
		}
		public struct Material {
			public float[] ambient, diffuse, specular, emissive;
			public uint illum;
			public float alpha;
			public Material(float[] ka, float[] kd, float[] ns, float[] em, uint illum, float alpha){
				this.ambient = ka;
				this.diffuse = kd;
				this.specular = ns;
				this.emissive = em;
				this.illum = illum;
				this.alpha = alpha;
			}
		}
	}
 
	/// <summary>
	/// Description of MyClass.
	/// </summary>
	/// 
	public class Loader {
		// ELEMENTS
		private List<Vertex> Vertices;
		private List<Normal> Normals;
		private List<Texture> Textures;
		private List<Face> Faces;
		// END ELEMENTS
 
		// LOADING ENGINE
		private System.IO.StreamReader fHandle;
		private System.IO.BinaryReader fs;
		private string filename;
		// END LOADING ENGINE
 
		public Loader(){
 
		}
 
		public bool Open(string filename){
			this.filename = filename;
			fHandle = new System.IO.StreamReader(this.filename);
			return true;
		}
 
		public void Close(){
			fs.Close();
			fHandle.Close();
		}
 
		public void parse3DS(){
			string ext = filename.Substring(filename.LastIndexOf('.')+1);
			ushort l_chunk_id;
			uint l_chunk_length;
			ushort l_qty;
			ushort l_face_flags;
			if(fHandle == null){
				System.Windows.Forms.MessageBox.Show("Please open a file first using Open() call");
				return;
			}
			using(fs = new System.IO.BinaryReader(fHandle.BaseStream)){
				while(fs.BaseStream.Position < fs.BaseStream.Length){
					l_chunk_id = fs.ReadUInt16();
					l_chunk_length = fs.ReadUInt32();
					if(fs.BaseStream.Position + 6 < fs.BaseStream.Length){
						fs.BaseStream.Position -= 4;
					}
					switch(l_chunk_id){
						case 0x4d4d:
							System.Windows.Forms.MessageBox.Show("MAIN");
							break;
						case 0x3d3d:
							System.Windows.Forms.MessageBox.Show("3D EDITOR");
							break;
						case 0x4000:
							System.Windows.Forms.MessageBox.Show("OBJECT");
							break;
						case 0x4100:
							System.Windows.Forms.MessageBox.Show("TRIANGULAR MESH");
							break;
						case 0x4110:
							System.Windows.Forms.MessageBox.Show("VERT LIST");
							break;
						case 0x4120:
							System.Windows.Forms.MessageBox.Show("FACES");
							break;
						case 0x4140:
							System.Windows.Forms.MessageBox.Show("MAPPING COORDINATE");
							break;
						default:
							break;
					}
				}
			}
		}
 
	}
 
}

Thanks for your help.


Comments

Comment viewing options

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

Use Matrix4.LookAt(Vector3 eye, Vector3 target, Vector3 up) then call GL.LoadMatrix(ref Matrix4) on the result.

Is that what you're after or did you want to compose the LookAt matrix based on rotations?

Cheers

silentcoder89's picture

Reibisch,

I am acknowledged on the syntax of Matrix4.LookAt and it's usage. My problem is, i'm not sure how to put the value of the eye, target and up vector.
Lets say i have the front view (which is the blue face of the cube). So, how do set the viewing matrix of the side/top view so that the side view will show the red face of the cube, and the top view will show the light green face of the cube?

I tried to play around with the

Matrix4 modelview = Matrix4.LookAt(new Vector3(0f,0.0f,-1f), new Vector3(0f,0.0f,1f), Vector3.UnitY);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref modelview);

by changing the value of the eye and the center vector. But it didn't result as i wanted. If i changed it to

Matrix4 modelview = Matrix4.LookAt(new Vector3(-20f,0.0f,-1f), new Vector3(0f,0.0f,1f), Vector3.UnitY);

i can set the view to the side, but the cube was placed too far away.

silentcoder89's picture
reibisch wrote:

Is that what you're after or did you want to compose the LookAt matrix based on rotations?

Yes, if it is possible, how?

silentcoder89's picture

Never mind. I've found the way to use it. :)

For Front, i used eye = Vector3(6.0f,6.0f,6.0f) and center = Vector3(0f,0f,0f)
For Side, i used eye = Vector3(-6.0f,0.0f,0.0f) and center = Vector3(0f,0f,0f)
For Top, i used eye = Vector3(-0.1f,6.0f,0.0f) and center = Vector3(0f,0f,0f)

Although the Top matrix slightly wrong, but i'll take care of it later.

Thanks Guys.

I really love OpenTK!

reibisch's picture

From your composition of the eye vector for the 'top' view, I'd wager that you're also using Vector3.UnitY as the 'up' vector. Try using Vector3.UnitZ instead (and get rid of the 0.1 in the x of the eye).

silentcoder89's picture

Is it? Okay, i'll give it a try.

BTW, here's the latest version of my app :

I've added my custom made 3DS model reader.

AttachmentSize
opntk.JPG75.07 KB
silentcoder89's picture
reibisch wrote:

From your composition of the eye vector for the 'top' view, I'd wager that you're also using Vector3.UnitY as the 'up' vector. Try using Vector3.UnitZ instead (and get rid of the 0.1 in the x of the eye).

It's working. Thanks for the addon, reibisch.