camera problems

Hi there!

These days I’m strugling with a fps camera style.

here’s my camera code


void Camera::FirstPerson(SDL_Window* window)
{
	SDL_GetMouseState(&mousex, &mousey);
	SDL_GetWindowSize(window, &screenwidth, &screenheight);
	if (xzdir > 360)
		xzdir -= 360;
	if (xzdir < 0)
		xzdir += 360;

	if (ydir > 180)
		ydir -= 180;
	else if (ydir < 0)
		ydir += 180;
	xzdir += screenwidth / 2 - mousex;
	ydir += screenheight / 2 - mousey;
	gluLookAt(x, y + 32, z, x + cos(degtorad(xzdir)), y + 32 + cos(degtorad(ydir)), z + sin(degtorad(xzdir)), 0, 1, 0);

	cout << cos(degtorad(xzdir)) << " " << screenwidth/2 << " " << ydir << endl;
	SDL_WarpMouseInWindow(window, screenwidth / 2, screenheight / 2);
}

and I run it here


Camera Player(0, 0, 0);
void Game::Draw()
{
	Clear();

	//Player.FirstPerson(window);
	Shape::DrawPlane(-0.5f,  -0.5f,  -0.5f, + 0.5f, 0.5f, 0.5f);

	SDL_GL_SwapWindow(window);
}

But when I uncomment the Player.FirstPerson(window); who is camera process it wont work.

Seeing sin and cos in a LookAt function makes me nervous. At some point, you may want to check out my Gimbal Lock video which discusses the problem with storing orientation as 3 separate values.

I’m also prejudiced against the mouse; so, I’m not the best person to ask about how to use a mouse in game programming. It’s a long story. But you’ll pretty much never see mouse code in my software except where I can get away with using a graphics tablet as if it were a mouse. That’s not to say you shouldn’t use a mouse. I think most users prefer it. I can’t imagine a PC game shipping without mouse support. Even I might break down and include it for a commercial game. Although personally I’m a big fan of the game pad for games.

Anyway, that aside, here’s the code from my elementary game engine which has a first person camera.



//=====================================================================================================================
//  Game::Update()
//
//	Purpose: 
//		To do everything that needs to be done in one frame except draw stuff to the screen.
//
//	Input:
//		float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
//	Output:
//		None.
//
//  Notes:
//		This is where most of your game code will be. It gets called every frame to change anything that needs to be changed
//	or done during that frame. It runs in a loop that needs to be called at least 30 times per second but there's nothing
//	to control how often it gets called. Things would move at unpredictable rates if we did not use TimeDelta to take in to
//	account the amount of time that has passed since the last frame.
//
//		We start out by processing the keyboard and game controller input to change the camera's position and direction. You
//	can also toggle full screen on and off.
//
//		The camera movement this frame is stored as a 3D vector that we treat more like a 2D vector. The facing normal should 
//	point in the direction we want the camera to face. And as a normal should have a length of 1. Any movement during the 
//	frame is cumulative from the various controls. When you move it uses either the CameraFacingNormal or a normal rotated 90
//	degrees away from the camera facing. It's basic vector addition to add the movement to the camera position.
//
//		XMMatrixLookAtRH is used to create a view matrix to simulate the camera every frame. Generally, I would say it is a 
//	good idea to not continuously recreate the view matrix but it's easier then maintaining a view matrix between frames and
//	this is really early in this tutorial series.
//
//		Finally some very simple rigid animation is thrown in to show you not only how to do it, but that it can be done and how
//	easy it is to do. Experiment by turning the rotations off and on and changing their directions and speed. 
//
//		The scene is lit with a simple Blinn-Phong shader that has "directional" lighting as opposed to point lights or
//	spot lights. Directional lighting is nothing more than a direction that the light shines in. It is a normalized vector
//	describing a direction and it has a color. That's all it is. Look at the shader for more detail. By rotating that direction
//	the light source seems to orbit the scene similar to a day and night cycle except the light shines through solid objects.
//
//=====================================================================================================================
void Game::Update()
{
	const float MaxTiltAngle = glm::radians(45.0);
	const unsigned char* Buttons;
	int JoyStick1Present = false;
	int NumberOfJoyStickAxes = 0;
	int NumberOfJoyStickButtons = 0;
	const float* AxesArray = nullptr;
	float LeftThumbStickY = 0.0f;
	float LeftThumbStickX = 0.0f;
	float Triggers = 0.0f;	//XBox 360 controller triggers are a single axis for both triggers. Positive = Left. Negative = Right.
	float RightThumbStickY = 0.0f;
	float RightThumbStickX = 0.0f;
	bool AButton = false;
	bool BButton = false;
		

	if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_ESCAPE && OperatingSystem.Keyboard.ActionPressed == GLFW_PRESS) OperatingSystem.ShutDown();
	
	JoyStick1Present = glfwJoystickPresent(GLFW_JOYSTICK_1);
	if (JoyStick1Present)
	{
		AxesArray = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &NumberOfJoyStickAxes);
		Buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &NumberOfJoyStickButtons);
		LeftThumbStickY = AxesArray[0];
		LeftThumbStickX = AxesArray[1];
		Triggers = AxesArray[2];
		RightThumbStickY = AxesArray[3];
		RightThumbStickX = AxesArray[4];

		//Camera Controls with XBox 360 controller.
		if (RightThumbStickX > 0.2 || RightThumbStickX < -0.2) View = glm::rotate(glm::mat4(), RightThumbStickX *0.06f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
		if (LeftThumbStickX > 0.2 || LeftThumbStickX < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -LeftThumbStickX * 0.1f)) * View;	//*0.1f to slow it down. Negative to flip the axis. -0.2 for deadzone.

		if (RightThumbStickY > 0.2 || RightThumbStickY < -0.2) CameraTilt += 0.03 * RightThumbStickY;
		if (LeftThumbStickY > 0.2 || LeftThumbStickY < -0.2) View = glm::translate(glm::mat4(), glm::vec3(-LeftThumbStickY * 0.1f, 0.0f, 0.0f)) * View;

		if (Triggers > 0.2 || Triggers < -0.2) View = glm::translate(glm::mat4(), glm::vec3(0.0f, Triggers*0.1f, 0.0f)) * View;

		if (Buttons[0] == '\x1') AButton = true;
		if (Buttons[1] == '\x1') BButton = true;
		if (Buttons[6] == '\x1') OperatingSystem.ShutDown();
	}


		//Camera Controls with keyboard.
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_W && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.05f)) * View;
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_S && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -0.05f)) * View;
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_E && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			CameraTilt += 0.1;
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Q && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			CameraTilt -= 0.1;
		if (OperatingSystem.Keyboard.ModePressed == GLFW_MOD_SHIFT)
		{
			//Keys while Shift keys are also held down.
			if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
				View = glm::translate(glm::mat4(), glm::vec3(0.1f, 0.0f, 0.0f)) * View;
			if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
				View = glm::translate(glm::mat4(), glm::vec3(-0.1f, 0.0f, 0.0f)) * View;
		}
		else
		{
			//Keys when shift keys are not being held down.
			if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_D && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			View = glm::rotate(glm::mat4(1.0f), 0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
			if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_A && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			View = glm::rotate(glm::mat4(1.0f), -0.05f, glm::vec3(0.0f, 1.0f, 0.0f)) * View;
		}
		
		
		//Yellow cube controls.
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_I && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 0.05f)));
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_K && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -0.05f)));
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_L && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 1.0f, 0.0f)));
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_J && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));


		if (CameraTilt > MaxTiltAngle) CameraTilt = MaxTiltAngle;
		if (CameraTilt < -MaxTiltAngle) CameraTilt = -MaxTiltAngle;
		
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Y && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube2Pivot =   glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;
		if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_H && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)
			Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;

		Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));
		Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));
		Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World;

	
}
//=====================================================================================================================



You can probably ignore the last 3 lines, as they merely put some objects in the scene and animate them. This is actually my Update() method that executes every frame. I haven’t bothered to create a separate camera or UI class. At some point I would probably want to for an actual game as I might want to allow the user to remap all the controls or such. But to get started, I think this is a pretty good starting place.

I’m using GLFW instead of GLU, but I imagine the concepts are pretty similar. This is OGL 4.5. I wasn’t sure which version you are using.

Notice there is no LookAt() method. That’s actually used once at startup in the Initialize() method:



//=====================================================================================================================
//  Game::Initialize()
//
//	Purpose: 
//		To allow any code you want to run once at startup not associated with art assets.
//
//	Input:
//		None.
//
//	Output:
//		bool - The program will close if it returns false assuming a catastrophic error has occured.
//
//  Notes:
//		
//
//=====================================================================================================================
bool Game::Initialize()
{
	bool GameObjectInitializedProperly = false;		//Must be set to true to keep the program from closing.
	

	CameraHeight = 1.68f;		//Roughly the average eye height in meters of an average man. Our camera will stay at this level to make it feel like we are in the scene.
	CameraTilt = 0.0f;			//Will tilt the camera up and down.

	GameObjectInitializedProperly = true; //This should probably be set by error checking, but we don't have any error checking here.
	View = glm::lookAt(glm::vec3(0.0f, CameraHeight, 2.0f), glm::vec3(0.0f, CameraHeight, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));	//1.63 meters is roughly the height of the average man's eyes.
	//Projection = glm::perspective(0.96f, 1.770833f, 0.1f, 700.0f);	//0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.
	Projection = glm::perspective(0.96f, OperatingSystem.AspectRatio(), 0.1f, 700.0f);	//0.96 is 55 degrees and 1.7708333 is the width to height ratio on my computer.

	glEnable(GL_CULL_FACE);		//Turn back face culling on.
	glCullFace(GL_BACK);		//Set back face culling to remove the back face rather than the front face. Back is default. Included here for clarity.
	glEnable(GL_DEPTH_TEST);	//Enable the depth buffer so that things are drawn correctly.
	glEnable(GL_FRAMEBUFFER_SRGB);	//Needed to prepare frame buffer for sRGB color.
	

	DiffuseLightDirection = glm::normalize(glm::vec3(1.0f, -1.0f, -1.0f));	//Direction that the primary light of the scene is "shining" in.
	AmbientLightColor = glm::vec4(0.05f, 0.05f, 0.1f, 1.0f);					//Light color in the "shadows".
	DiffuseLightColor = glm::vec4(1.0f, 1.0f, 0.9f, 1.0f);					//Direct light color.

	return GameObjectInitializedProperly;
}
//=====================================================================================================================

Basically, I’m using the View matrix to be the camera and thus hold the camera’s position and orientation information from frame to frame. The UI merely adjusts the View matrix to rotate the camera.

I do a trick that I kind of like to limit the camera to tilt 45 degrees up or down. It’s largely implemented in my Draw() method:


	glm::mat4 TiltedView = glm::rotate(glm::mat4(), CameraTilt, glm::vec3(1.0, 0.0, 0.0)) * View;

I’m actually drawing with “TiltedView” rather than the View matrix. So, at the very last minute before drawing, I apply the camera tilt. So, I’m not really ever tilting the camera up or down as the view matrix, just moving it through the scene. The yaw rotation is maintained in the View matrix. The pitch is kept separately in order to allow me to limit it easily. In the Update() a simple less than greater than keeps the tilt in range. I never roll the camera. It’s the best way I’ve come up with so far that uses the View matrix to control the camera and limits the range of motion.

Anyway, this is one way of doing it. I think it’s one of the more efficient ways of doing it; you’ll notice the camera code is so simple that much of it’s part of the same line of code as the UI. Thought it might give you some ideas you could work with.

Some things that are really missing here are a timer to use the time since the last frame occurred to keep animation at a constant rate. And so the rates are hard coded. That’s probably not good, but it works to get started.

But all the code is there for a game pad or keyboard. You just need to add code for a mouse which shouldn’t be too much different than what’s already there.

The complete Visual Studio 2015 project is on my website at VirtuallyProgramming.com available for download. So, you can see the code in action. There’s also a video clip on that page where you can see what the code does without downloading the actual code.

EDIT: Just noticed that some of the comments in the code are way out in left field, especially in the headers. I copy and paste code from one version to the next and that Update() header looks like it came from a DirectX version of the code that worked differently. So take some of the comments with a grain of salt.

Impresive, I got my system actually worked, but I forgot to add things in drawplane. So I never used glm, because I considered my math is enough, anyways, I want to know how does your system works, because you lost me with some things, for example, can you tell me what is the magic key, in your system?

Good to hear it’s working!

“Magic key”? I’m not sure what you mean. There’s a lot of stuff like “OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_W”. GLFW handles keyboard input, but it’s not exactly written in an object oriented way. So, I encapsulated it as part of the OperatingSystem object which is an instance of the OSClass class. OSClass has a public object “Keyboard” of the KeyboardClass class. These are all single instance objects. But this makes the Keyboard object visible from the OperatingSystem object. The Game class inherits from the OGLGameClass. I think it’s the only inheritance I have going on. The OGLGameClass encapsulates everything that makes up the engine in a non-operating system sort of way, pretty much and it defines rules on how the Game class works. Only the Game class should need to be modified to create a game. The other classes are meant to be a permanent framework that doesn’t really change from project to project. The OGLGameClass has a protected instance of the OSClass, exposing it through that class. If you start with Main.cpp and read the code through, it should make sense. Main.cpp is there to start the program and hold everything together.

If you mean the core concept of how I’m controlling the camera, it’s merely using the View matrix and letting it hold the position and orientation of the camera at all times. (Of course I cheated that a little with the CameraTilt variable to limit the X axis rotation.)

The complete working source code is available on my website at VirtuallyProgramming.com and it’s highly commented/annotated. Although it is a “work in progress”. It’s a re-write of my DirectX 11 engine, which is also on the website. With the DX engine there were things done a little differently partially because it was DX and partially because I’ve learned a thing or two over the past couple of years since I wrote the DX engine. (It’s a very basic engine and hardly does enough to qualify as an “engine”, but it’s the basic code of the type you would use in every project no matter what the project is.) And it looks like I copied over the headers from each method as I recreated it, so that some of my DX comments are still in the OGL code. I haven’t gotten the OGL engine to a “ready” state. But I published the code that I have so far before it’s truly ready. To be truly ready, I need to go back and correct all the comments among other things. I also plan on putting in a written error log and the ability to load models from Blender. (The DX engine has the ability to load models from Blender; I just haven’t put that code in the OGL version yet.) But the code is there to pull apart, run, and experiment with.

If you have specific questions about it, I would be glad to answer them.

If you’re good with vector and matrix algebra, you may not care, but my videos on vectors and matrices pretty much explain my thoughts on using matrices.