flopoloco's picture

OpenTK First Person Camera

Here is an example about how a first person camera works.

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using Debug = System.Diagnostics.Debug;
 
namespace OpenTKCamera
{
	static class GLDraw
	{
		static GLDraw()
		{
		}
 
		public static void Axis()
		{
			GL.LineWidth(2f);
			GL.Begin(PrimitiveType.Lines);
 
			GL.Color3(1f, 0f, 0f);
			GL.Vertex3(0f, 0f, 0f);
			GL.Vertex3(1f, 0f, 0f);
 
			GL.Color3(0f, 1f, 0f);
			GL.Vertex3(0f, 0f, 0f);
			GL.Vertex3(0f, 1f, 0f);
 
			GL.Color3(0f, 0f, 1f);
			GL.Vertex3(0f, 0f, 0f);
			GL.Vertex3(0f, 0f, 1f);
 
			GL.End();
			GL.LineWidth(1f);
		}
 
		public static void WireCube(float size = 0.5f)
		{
			// Bottom
			GL.Begin(PrimitiveType.LineLoop);
			GL.Vertex3(-size, -size, -size);
			GL.Vertex3( size, -size, -size);
			GL.Vertex3( size, -size, size);
			GL.Vertex3(-size, -size, size);
			GL.End();
 
			// Top
			GL.Begin(PrimitiveType.LineLoop);
			GL.Vertex3(-size, size, -size);
			GL.Vertex3( size, size, -size);
			GL.Vertex3( size, size,  size);
			GL.Vertex3(-size, size,  size);
			GL.End();
 
			// Vertical
			GL.Begin(PrimitiveType.Lines);
			GL.Vertex3(-size, -size, -size);
			GL.Vertex3(-size,  size, -size);
			GL.Vertex3( size, -size, -size);
			GL.Vertex3( size,  size, -size);
			GL.Vertex3( size, -size, size);
			GL.Vertex3( size,  size, size);
			GL.Vertex3(-size, -size, size);
			GL.Vertex3(-size,  size, size);
			GL.End();
		}
 
		public static void Grid(float iterations = 10, float space = 5)
		{
			GL.Color3(1f, 1f, 1f);
 
			var gridBound = iterations * space;
 
			GL.Begin(BeginMode.Lines);
 
			for (var x = -iterations; x <= iterations; x++)
			{
				GL.Vertex3(x * space, 0, -gridBound);
				GL.Vertex3(x * space, 0,  gridBound);
 
				for (var z = -iterations; z <= iterations; z++)
				{
					GL.Vertex3(-gridBound, 0, z * space);
					GL.Vertex3( gridBound, 0, z * space);
				}
			}
 
			GL.End();
		}
	}
 
	class FirstPersonCamera
	{
		public Vector3 Position;
		public Vector3 Rotation;
		public Quaternion Orientation;
 
		public Matrix4 Matrix;
		public Matrix4 Model;
		public Matrix4 Projection;
 
		public FirstPersonCamera()
		{
			Matrix = Matrix4.Identity;
			Projection = Matrix4.Identity;
			Orientation = Quaternion.Identity;
		}
 
		public void Update()
		{
			Orientation =
				Quaternion.FromAxisAngle(Vector3.UnitY, Rotation.Y) *
				Quaternion.FromAxisAngle(Vector3.UnitX, Rotation.X);
 
			var forward = Vector3.Transform(Vector3.UnitZ, Orientation);
			Model = Matrix4.LookAt(Position, Position + forward, Vector3.UnitY);
			Matrix = Model * Projection;
		}
 
		public void Resize(int width, int height)
		{
			Projection = Matrix4.CreatePerspectiveFieldOfView(
				MathHelper.PiOver4, (float)width/height, 0.1f, 1000f
			);
		}
 
		public void TurnX(float a)
		{
			Rotation.X += a;
			Rotation.X = MathHelper.Clamp(Rotation.X, -1.57f, 1.57f);
		}
 
		public void TurnY(float a)
		{
			Rotation.Y += a;
			Rotation.Y = ClampCircular(Rotation.Y, 0, MathHelper.TwoPi);
		}
 
		public void MoveX(float a)
		{
			Position += Vector3.Transform(Vector3.UnitX * a, Quaternion.FromAxisAngle(Vector3.UnitY, Rotation.Y));
		}
 
		public void MoveY(float a)
		{
			Position += Vector3.Transform(Vector3.UnitY * a, Quaternion.FromAxisAngle(Vector3.UnitY, Rotation.Y));
		}
 
		public void MoveZ(float a)
		{
			Position += Vector3.Transform(Vector3.UnitZ * a, Quaternion.FromAxisAngle(Vector3.UnitY, Rotation.Y));
		}
 
		public void MoveYLocal(float a)
		{
			Position += Vector3.Transform(Vector3.UnitY * a, Orientation);
		}
 
		public void MoveZLocal(float a)
		{
			Position += Vector3.Transform(Vector3.UnitZ * a, Orientation);
		}
 
		public static float ClampCircular(float n, float min, float max)
		{
			if (n >= max) n -= max;
			if (n < min) n += max;
			return n;
		}
	}
 
	class MainClass
	{
		public static void Main(string[] args)
		{
			var window = new GameWindow(800, 600);
 
			var camera = new FirstPersonCamera();
			camera.Position = new Vector3(0, 5, 0);
 
			var mouseSpeed = new Vector2();
			var mouseSpeedValue = 0.5f;
 
			window.Load += (object sender, EventArgs e) => 
			{
				GL.Enable(EnableCap.DepthTest);
				GL.ClearColor(System.Drawing.Color.CornflowerBlue);
			};
 
			window.Resize += (object sender, EventArgs e) => 
			{
				camera.Resize(window.Width, window.Height);
			};
 
			Func<Key, Key, float, float> checkKeyState = (_keyA, _keyB, _value) =>
			{
				if (window.Keyboard[_keyA]) return  _value;
				if (window.Keyboard[_keyB]) return -_value;
				return 0f;
			};
 
			window.UpdateFrame += (object sender, FrameEventArgs e) =>
			{
				var time = (float)e.Time;
				var moveSpeed = 5*time;
 
				mouseSpeed.X = window.Mouse.YDelta * mouseSpeedValue * time;
				mouseSpeed.Y = window.Mouse.XDelta * mouseSpeedValue * time;
 
				if (window.Mouse[MouseButton.Left])
				{
					camera.TurnX( mouseSpeed.X);
					camera.TurnY(-mouseSpeed.Y);
				}
 
				camera.MoveX(checkKeyState(Key.A, Key.D, moveSpeed));
 
				if (window.Keyboard[Key.ControlLeft])
				{
					camera.MoveYLocal(checkKeyState(Key.Space, Key.C, moveSpeed));
					camera.MoveZLocal(checkKeyState(Key.W, Key.S, moveSpeed));
				}
				else
				{
					camera.MoveY(checkKeyState(Key.Space, Key.C, moveSpeed));
					camera.MoveZ(checkKeyState(Key.W, Key.S, moveSpeed));
				}
 
				camera.Update();
			};
 
			window.RenderFrame += (object sender, FrameEventArgs e) =>
			{
				GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
				GL.LoadMatrix(ref camera.Matrix);
				GLDraw.Grid();
				GLDraw.Axis();
				window.SwapBuffers();
			};
 
			window.Run();
		}
	}
}

Comments

Comment viewing options

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

Good implementation, thanks for sharing, I have a question though, how to make this camera isometric (orthogonal view)?

burhan's picture

Ok, I found out how to do it, for anyone need this, here's how:

I replaced this code:

Projection = Matrix4.CreatePerspectiveFieldOfView(
				MathHelper.PiOver4, (float)width/height, 0.1f, 1000f
			);

with:

Projection = Matrix4.CreateOrthographic(glControl1.Width, glControl1.Height, 0, 1000);

Oppai's picture

Your post is an excellent example of why I keep coming back to read your excellent quality content.
how to get more likes on soundcloud