Preventing Camera from being Upside down ...

Hello,

I have a rather interesting problem; on random occasions my camera, via the view matrix I believe, appears “upside down”.

The camera will be pointing in the right direction and at the right position but the view completely inverted.

I feel a little stupid but is there an easy way to reset the view matrix such that the camera will be upright? Sometimes I get the camera information from loaded geometry so it isn’t necessarily something I have complete control over.

Thank you.

It’s rather hard to know why that would happen without knowing useful information. Like how you’re generating your camera matrix.

That is a good point.

Here is my OpenGLCamera class (both header and implementation):

OpenGLCamera.h:


#ifndef OPENGLCAMERA_H_
#define OPENGLCAMERA_H_

#include <string>
#include <ctime>

#ifndef OPENGL_INCLUDES_
#define OPENGL_INCLUDES_

// include GLEW extension handling library
#include "GL/glew.h"

#endif

#ifndef GLM_INCLUDES_
#define GLM_INCLUDES_

#include <glm/glm.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>

#endif

class OpenGLCamera;

class OpenGLCamera {

private:

protected:

	glm::mat4 CurrentCameraMatrix;
	glm::mat4 CurrentProjectionMatrix;

	glm::vec3 CurrentCameraPosition;
	glm::vec3 CurrentViewingPosition;
	glm::vec3 CurrentCameraPositionDelta;
	glm::vec3 CurrentViewingDirection;
	glm::vec3 CurrentRightDirection;
	glm::vec3 CurrentUpDirection;

	glm::vec3 HomeCameraPosition;

	GLfloat FieldOfView;

	bool FixedAspectRatio;
	GLfloat AspectRatio;

	GLfloat NearPlane;
	GLfloat FarPlane;

	GLfloat CurrentMovementSpeed;
	GLfloat CurrentMovementDamping;
	GLfloat CurrentMouseMovementSpeed;

	GLfloat HorizontalAngle;
	GLfloat VerticalAngle;

	GLfloat* deltaTime;

	GLint* CurrentScreenWidth;
	GLint* CurrentScreenHeight;

	std::string NameOfCamera;

public:

	OpenGLCamera();
	OpenGLCamera(glm::vec3 InitialPosition);

	~OpenGLCamera();

	virtual void RecalibrateCamera();

	virtual void SetCurrentMousePosition(double MouseX, double MouseY);
	virtual void SetScreenPointers(GLint* WidthPointer, GLint* HeightPointer);
	virtual void SetDeltaTimePointer(GLfloat* DeltaTimePointerToSet);
	virtual void SetCameraPosition(glm::vec3 NewCameraPosition);
	virtual void MoveToHomeCameraPosition();
	virtual void SetHomeCameraPosition(glm::vec3 HomeCameraPositionToSet);
	virtual void SetLookAt(glm::vec3 NewCameraLookAt);
	virtual void SetUp(glm::vec3 NewCameraUp);
	virtual void CalculateProjection();
	virtual void CalculateView();
	virtual void Calculate();
	virtual void Calculate(GLint EyeToCalculate);
	virtual GLfloat GetScreenScaleX();
	virtual GLfloat GetScreenScaleY();
	virtual glm::mat4 GetProjectionViewMatrix();
	virtual glm::mat4* GetProjectionMatrixPointer();
	virtual glm::mat4* GetViewMatrixPointer();
	virtual glm::vec3* GetViewingDirection();
	virtual glm::vec3* GetCurrentPosition();
	virtual std::string GetName();
	virtual void SetName(std::string CameraNameToSet);
	virtual GLfloat CalculateAspectRatio();
	virtual void SetFOVRadians(GLfloat FOV);
	virtual void SetFOVDegrees(GLfloat FOV);
	virtual void SetNearPlane(GLfloat NearPlaneToSet);
	virtual void SetFarPlane(GLfloat FarPlaneToSet);
	virtual void SetAspectRatio(GLfloat AspectRatioToSet);
	virtual void RotateLeft();
	virtual void RotateRight();
	virtual void MoveFaster();
	virtual void MoveSlower();
	virtual void MoveUp();
	virtual void MoveDown();
	virtual void MoveForward();
	virtual void MoveBackward();
	virtual void MoveLeft();
	virtual void MoveRight();
};

#endif

And here is the implementation:


#include "OpenGLCamera.h"

OpenGLCamera::OpenGLCamera() {

	CurrentCameraMatrix = glm::mat4(1.0f);
	CurrentProjectionMatrix = glm::mat4(1.0f);
	
	FieldOfView = 55.0f;
	FixedAspectRatio = false;

	NearPlane = 1.0f;
	FarPlane = 6000.0f;

	CurrentMovementSpeed = 0.10f;
	CurrentMovementDamping = 0.85f;
	CurrentMouseMovementSpeed = 0.005f;

	HorizontalAngle = 3.14f;
	VerticalAngle = 0.0f;

	CurrentCameraPosition = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentViewingPosition = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentCameraPositionDelta = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentViewingDirection = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentRightDirection = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentUpDirection = glm::vec3(0.0f, 1.0f, 0.0f);

	HomeCameraPosition = glm::vec3(0.0f, 0.0f, 0.0f);

}

OpenGLCamera::OpenGLCamera(glm::vec3 InitialPosition) {

	CurrentCameraMatrix = glm::mat4(1.0f);
	CurrentProjectionMatrix = glm::mat4(1.0f);

	FieldOfView = 55.0f;
	FixedAspectRatio = false;

	NearPlane = 1.0f;
	FarPlane = 1000.0f;

	CurrentMovementSpeed = 0.10f;
	CurrentMovementDamping = 0.85f;
	CurrentMouseMovementSpeed = 0.005f;

	HorizontalAngle = 3.14f;
	VerticalAngle = 0.0f;

	CurrentCameraPosition = InitialPosition;
	CurrentCameraPositionDelta = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentViewingDirection = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentRightDirection = glm::vec3(0.0f, 0.0f, 0.0f);
	CurrentUpDirection = glm::vec3(0.0f, 1.0f, 0.0f);

}

OpenGLCamera::~OpenGLCamera() {



}

void OpenGLCamera::RecalibrateCamera() {

	HorizontalAngle = 0.0f;
	VerticalAngle = 0.0f;

}

std::string OpenGLCamera::GetName() {

	return NameOfCamera;

}

void OpenGLCamera::SetName(std::string CameraNameToSet) {

	NameOfCamera = CameraNameToSet;

}

void OpenGLCamera::SetCameraPosition(glm::vec3 NewCameraPosition) {

	CurrentCameraPosition = NewCameraPosition;

}

void OpenGLCamera::SetLookAt(glm::vec3 NewCameraLookAt) {

	CurrentViewingDirection = NewCameraLookAt;

}

void OpenGLCamera::SetUp(glm::vec3 NewCameraUp) {

	CurrentUpDirection = NewCameraUp;

}

void OpenGLCamera::SetScreenPointers(GLint* WidthPointer, GLint* HeightPointer) {

	CurrentScreenWidth = WidthPointer;
	CurrentScreenHeight = HeightPointer;

}

void OpenGLCamera::SetDeltaTimePointer(GLfloat* DeltaTimePointerToSet) {

	deltaTime = DeltaTimePointerToSet;

}

void OpenGLCamera::SetCurrentMousePosition(double MouseX, double MouseY) {

	GLfloat CurrentMouseX = (GLfloat)MouseX;
	GLfloat CurrentMouseY = (GLfloat)MouseY;

	GLfloat ScreenWidth = (GLfloat)(*CurrentScreenWidth);
	GLfloat ScreenHeight = (GLfloat)(*CurrentScreenHeight);

	GLfloat ChangeInHorizontalAngle = CurrentMouseMovementSpeed * (*deltaTime) * (ScreenWidth / 2 - CurrentMouseX);
	GLfloat ChangeInVerticalAngle = CurrentMouseMovementSpeed * (*deltaTime) * (ScreenHeight / 2 - CurrentMouseY);

	GLfloat HorizontalAngleDelta = abs(ChangeInHorizontalAngle);
	GLfloat VerticalAngleDelta = abs(ChangeInVerticalAngle);

	if (HorizontalAngleDelta > 10.0f) {
		if (ChangeInHorizontalAngle > 0) {
			ChangeInHorizontalAngle = 10.0f;
		} else {
			ChangeInHorizontalAngle = -10.0f;
		}
	}

	if (VerticalAngleDelta > 10.0f) {
		if (ChangeInVerticalAngle > 0) {
			ChangeInVerticalAngle = 10.0f;
		}
		else {
			ChangeInVerticalAngle = -10.0f;
		}
	}

	HorizontalAngle += ChangeInHorizontalAngle;
	VerticalAngle += ChangeInVerticalAngle;

}

void OpenGLCamera::CalculateView() {

	CurrentCameraPosition += CurrentCameraPositionDelta;

	CurrentCameraPositionDelta *= CurrentMovementDamping;

	CurrentViewingDirection = glm::vec3(
		cos(VerticalAngle) * sin(HorizontalAngle),
		sin(VerticalAngle),
		cos(VerticalAngle) * cos(HorizontalAngle)
		);

	CurrentRightDirection = glm::vec3(
		sin(HorizontalAngle - 3.14f / 2.0f),
		0.0f,
		cos(HorizontalAngle - 3.14 / 2.0f)
		);

	CurrentUpDirection = glm::cross(CurrentRightDirection, CurrentViewingDirection);

	CurrentCameraMatrix = glm::lookAt(CurrentCameraPosition, CurrentCameraPosition + CurrentViewingDirection, CurrentUpDirection);

}

void OpenGLCamera::SetFOVRadians(GLfloat FOV) {

	FOV *= 2.0f;

	FieldOfView = FOV * 57.2957795f;

}

void OpenGLCamera::SetFOVDegrees(GLfloat FOV) {

	FieldOfView = FOV;

}

void OpenGLCamera::SetAspectRatio(GLfloat AspectRatioToSet) {

	FixedAspectRatio = true;

	if (AspectRatioToSet > 0) {

		AspectRatio = AspectRatioToSet;

	}

}

void OpenGLCamera::SetNearPlane(GLfloat NearPlaneToSet) {

	NearPlane = NearPlaneToSet;

}

void OpenGLCamera::SetFarPlane(GLfloat FarPlaneToSet) {

	FarPlane = FarPlaneToSet;

}

void OpenGLCamera::RotateLeft() {

	HorizontalAngle -= 10.0f;

}

void OpenGLCamera::RotateRight() {

	HorizontalAngle += 10.0f;

}

void OpenGLCamera::MoveFaster() {

	CurrentMovementSpeed += 0.15f;

}

void OpenGLCamera::MoveSlower() {

	CurrentMovementSpeed -= 0.15f;

	if (CurrentMovementSpeed < 0.10f) {

		CurrentMovementSpeed = 0.10f;

	}

}

void OpenGLCamera::MoveForward() {

	CurrentCameraPositionDelta += CurrentViewingDirection * (*deltaTime) * CurrentMovementSpeed;
}

void OpenGLCamera::MoveBackward() {

	CurrentCameraPositionDelta -= CurrentViewingDirection * (*deltaTime) * CurrentMovementSpeed;

}

void OpenGLCamera::MoveLeft() {

	CurrentCameraPositionDelta -= CurrentRightDirection * (*deltaTime) * CurrentMovementSpeed;

}

void OpenGLCamera::MoveRight() {

	CurrentCameraPositionDelta += CurrentRightDirection * (*deltaTime) * CurrentMovementSpeed;

}

void OpenGLCamera::MoveUp() {

	CurrentCameraPositionDelta += CurrentUpDirection * (*deltaTime) * CurrentMovementSpeed;

}

void OpenGLCamera::MoveDown() {

	CurrentCameraPositionDelta -= CurrentUpDirection * (*deltaTime) * CurrentMovementSpeed;

}

void OpenGLCamera::CalculateProjection() {

	CurrentProjectionMatrix = glm::perspective(
		FieldOfView,
		CalculateAspectRatio(),
		NearPlane,
		FarPlane
		);

}

void OpenGLCamera::Calculate() {

	CalculateProjection();
	CalculateView();

}

void OpenGLCamera::Calculate(GLint) {

	CalculateProjection();
	CalculateView();

}

GLfloat OpenGLCamera::CalculateAspectRatio() {

	if (!FixedAspectRatio) {

		if ((GLfloat)(*CurrentScreenWidth) > 0 && (GLfloat)(*CurrentScreenHeight) > 0) {

			AspectRatio = (GLfloat)(*CurrentScreenWidth) / (GLfloat)(*CurrentScreenHeight);

		}

		else {

			AspectRatio = 1.78f;

		}

	}

	return AspectRatio;

}

glm::mat4* OpenGLCamera::GetProjectionMatrixPointer() {

	return &CurrentProjectionMatrix;

}

glm::mat4* OpenGLCamera::GetViewMatrixPointer() {

	return &CurrentCameraMatrix;

}

glm::mat4 OpenGLCamera::GetProjectionViewMatrix() {

	return CurrentProjectionMatrix * CurrentCameraMatrix;

}

GLfloat OpenGLCamera::GetScreenScaleX() {

	return 2.0f / (float)(*CurrentScreenWidth);

}

GLfloat OpenGLCamera::GetScreenScaleY() {

	return 2.0f / (float)(*CurrentScreenHeight);

}

glm::vec3* OpenGLCamera::GetViewingDirection() {

	return &CurrentViewingDirection;

}

glm::vec3* OpenGLCamera::GetCurrentPosition() {

	return &CurrentCameraPosition;

}

void OpenGLCamera::MoveToHomeCameraPosition() {

	CurrentCameraPosition = HomeCameraPosition;

}

void OpenGLCamera::SetHomeCameraPosition(glm::vec3 HomeCameraPositionToSet) {

	HomeCameraPosition = HomeCameraPositionToSet;

}

I also need an answer to this. Using a quaternion rotational system for my camera. I can rotate around the x more than 180 and flip upside down, once there its hard to flip back up :D. I messed around with trying to lock the angle of rotation detecting too high angles but it would get stuck on my locking angles. So I wonder what is a good method gets popcorn

CalculateView() looks fine.

Is VerticalAngle “going over the top” (outside of the range -π/2 to +π/2)? If that happens, the view will “legitimately” be upside-down, i.e. you’re specifically requesting that the camera be rotated about the right vector until it’s facing backwards, at which point it will be upside-down.

[QUOTE=GClements;1263047]CalculateView() looks fine.

Is VerticalAngle “going over the top” (outside of the range -π/2 to +π/2)? If that happens, the view will “legitimately” be upside-down, i.e. you’re specifically requesting that the camera be rotated about the right vector until it’s facing backwards, at which point it will be upside-down.[/QUOTE]

Thanks for the review; the issue with the VerticalAngle variable is certainly a possibility.

As a test, I did the following and it works:


if (CurrentUpDirection.y > 0.0f) {

		CurrentUpDirection.y *= -1.0f;

	}

Based off of the above class snippit; it seems that my “Y” direction needs to be negative in order for my viewing to work correctly on screen.

Not sure why that is; perhaps in another location of my code the “Y” direction is being multiplied and set to be the opposite of what it should be.

But this hack fixes things for now.

What do you think? Thanks to all for looking at my code and your feedback.

You will want to trace that Y direction and find where that is occurring sooner rather than later IMO. It will really messed you up later on if you then forget to pre-multiply.