flopoloco's picture

OpenTK First Person Camera

using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
namespace OpenTKCameraPort
	class Program : GameWindow
		private float cameraSpeed = 5f;
		private Matrix4 cameraMatrix;
		private float [] mouseSpeed = new float[2];
		public Program() : base(1024, 768)
		protected override void OnLoad(EventArgs e)
			cameraMatrix = Matrix4.Translation(0f, -10f, 0f);
		protected override void OnRenderFrame(FrameEventArgs e)
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
			GL.LoadMatrix(ref cameraMatrix);
			// Display some planes
			for (int x = -10; x <= 10; x++)
				for (int z = -10; z <= 10; z ++)
					GL.Translate((float) x * 5f, 0f, (float) z * 5f);
						GL.Vertex3( 1f, 4f, 0f);
						GL.Vertex3(-1f, 4f, 0f);
						GL.Vertex3(-1f, 0f, 0f);
						GL.Vertex3( 1f, 0f, 0f);
		protected override void OnUpdateFrame(FrameEventArgs e)
			if (Keyboard[Key.W])
				cameraMatrix = Matrix4.Mult(cameraMatrix,
				             Matrix4.Translation(0f, 0f, 10f*(float)e.Time));
			if (Keyboard[Key.S])
				cameraMatrix = Matrix4.Mult(cameraMatrix,
				             Matrix4.Translation(0f, 0f, -10f*(float)e.Time));
			if (Keyboard[Key.A])
				cameraMatrix = Matrix4.Mult(cameraMatrix,
				                            Matrix4.Translation(10f*(float)e.Time, 0f, 0f));
			if (Keyboard[Key.D])
				cameraMatrix = Matrix4.Mult(cameraMatrix,
				                            Matrix4.Translation(-10f*(float)e.Time, 0f, 0f));
			mouseSpeed[0] *= 0.9f;
			mouseSpeed[1] *= 0.9f;
			mouseSpeed[0] += Mouse.XDelta / 100f;
			mouseSpeed[1] += Mouse.YDelta / 100f;
			cameraMatrix = Matrix4.Mult(cameraMatrix, Matrix4.RotateY(mouseSpeed[0]*(float)e.Time));
			cameraMatrix = Matrix4.Mult(cameraMatrix, Matrix4.RotateX(mouseSpeed[1]*(float)e.Time));			
			if (Keyboard[Key.Escape])
		protected override void OnResize(EventArgs e)
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
            Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1.0f, 64.0f);
            GL.LoadMatrix(ref projection);
		public static void Main(string[] args)
			using (Program p = new Program())

Hello, I would like please some help. I have made this first person camera example. I have

1. How can set the cursor position to a fixed position.
I tried centering the cursor to the middle of the screen
System.Windows.Forms.Cursor.Position = new Point(Bounds.Left + (Bounds.Width / 2), Bounds.Top + (Bounds.Height / 2));
But after that OpenTK can not get mouse delta.

cameraMatrix = Matrix4.Translation(0f, -10f, 0f);
I set -10 for setting the camera above the planes (I guess it would be better to set it to positive for up and negative for down)

3. How can I prevent Z rotation rolling (I want to make it a turntable rotation)


Comment viewing options

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

Hi Fiddler,

This still doesn't work for me, but I'll keep messing around with it. It seems like every time I reset the cursor position, it resets the Mouse.X and Mouse.Y as well, which resets my view. This is taking a lot of effort to solve :P

the Fiddler's picture

Yes, that's supposed to happen (resetting Mouse.X and Mouse.Y, not taking a lot of effort). Mouse.XY and Cursor.Position.XY are views of the same system information, the first in window coordinates the latter in screen coordinates. Whatever happens to one of them affects the other immediately.

If you need to persist the values of Mouse.X/Y, read them in UpdateFrame and store them somewhere.

TheNerd's picture

Hmm so that's my point (and currently my problem).

Lets say we have a 600,600 screen. Center is 300,300.

We move the mouse to the left 10 points. Now Mouse.X should be 290. Update frame comes in, grabs it. We call reset, and now Mouse.X is back to 300. The frame is rendered using the values 290 (because we stored them).

update frame gets called again SUPER quickly, before the human has moved. Since it was centered, its mouse.X is 300. Its reset again, but that doesn't make much difference. Then the frame is rendered, and the camera is slammed back to the center of the screen.

A line by line trace using debugging shows that this is what seems to be occurring with my program. Is that not what happens with yours???

-The Nerd

the Fiddler's picture

No, because I use mouse_delta to rotate the camera. If mouse_delta is zero, no rotation so it works.

In general, I avoid using absolute mouse positions to move a camera, they tend to be too "jumpy" (plus they change with the resolution, high resolution = higher movement). I tend to use the mouse deltas from the center of the window, normalized to [-1.0, 1.0] and multiplied by some "mouse speed" factor (default is 1, can be increased/decreased by the user).

TheNerd's picture

I think I'm still missing something - because the delta will change when the mouse pointer moves back to the center of the screen. The only time the delta is zero is if you are subtracting the current position from the center point every time. If you subtract the current point from the last point, after the move back to center, you will get a new delta for that move. This seems to be what happens to me when I implement the above code with my code for doing the rotation.

My code for the rotation (calcDelta is your exact code from above, Fiddler):

In the updateframe:

Point newDelta = CalcDelta();
XRotation += (float)newDelta.Y;   
 YRotation += (float)newDelta.X;

In the render:

            GL.LoadMatrix(ref modelview);
            GL.Rotate(XRotation, 1.0, 0.0, 0.0);  
            GL.Rotate(YRotation, 0.0, 1.0, 0.0);  
            GL.Translate(-location.X, -location.Y, -location.Z);  //translate the screen to the position of our camera
TheNerd's picture

Finally solved it. When you said that you measure the delta from the window center each time, I didn't understand at first. NOW I do. I implemented this in the calcDelta:

 mouse_delta = new Point(
                    mouse_current.X - PointToClient(windowCenter).X,
                    mouse_current.Y - PointToClient(windowCenter).Y);

And now it works. It's jerky like you said because I am not normalizing it and using a multiplier (yet) but at least I can get the mouse to rotate the view, FINALLY.

killa's picture

I made very simple solution for first peson view (migh need small fix with pitch)

Declare this:

Vector2 current, 
                mouseAdd ;

and add this in OnUpdateFrame:

current.X = Mouse.X;
            current.Y = Mouse.Y;
            if (current.X != Width / 2)
                mouseAdd.X = ((Width / 2) - current.X) / 1000;
                facing -= mouseAdd.X; 
            if (current.Y != Height / 2)
                mouseAdd.Y = ((Height / 2) - current.Y) / 1000;
                pitch -= mouseAdd.Y;
            System.Windows.Forms.Cursor.Position = new System.Drawing.Point(Bounds.Left + (Bounds.Width / 2), Bounds.Top + (Bounds.Height / 2));
spox's picture

I also have a problem with this, and because im a beginner i just cant figure out how to fix it. I red the topic a few times and i still couldnt make my code work properly. When i move the mouse, it calculates the new delta by substracting the current position from the old one, and everything works out good so far, but when i reset the mouse, the camera goes back to where it was, even if im not using the Mouse.Move event... Could anybody help me please :(!

gigimoi's picture

The pitch wasn't working properly for me, it would seem to slow(and then invert) near the bottom and top.

Vector3 lookatPoint = new Vector3((float)Math.Cos(facing), pitch + (1/2) * (pitch*pitch), (float)Math.Sin(facing));

This is close enough to fixing it.

lid6j86's picture

Just found this and it's helped tremendously so far, I was just wondering if all if this is still up to date, or if parts have been depreciated / newer ways have been developed since the post was first made?