FPS-style Camera - c++

I'm new to GLM and trying to write a fps-style camera class
should I need to recalculate the up vector when I move the mouse?
however everything look strange after i change the up vector
Still, my program looks buggy when i maintain the up vector
file: fpscamera.cpp
void FPSCamera::rotateScreen(float dx, float dy){
auto oldDirection=getTarget()-getPosition();
setTarget(getPosition()+rotate(oldDirection, -dx*5, getUp()));
auto oldHoro=rotate(cross(oldDirection,getUp()), -dx*5, getUp());
setTarget(getPosition()+rotate(getTarget()-getPosition(),dy*5, oldHoro));
////////////////HERE////////////////////////
//setUp(normalize(cross(oldHoro,getTarget()-getPosition())));
}
You can download the source code and binary here
https://docs.google.com/file/d/0B9givuJvSet8ekRReWtRM29ldzg/edit?usp=sharing
mingw-built64 g++4.8(C++11) glfw3 GLM(MathHelper Library) required
g++ -std=c++0x -Wall main.cpp mesh.cpp fpscamera.cpp -lglfw3 -lopengl32 -lglu32 -lgdi32
move toward the target... W
turn Left/Right.... Q/E
move left/Right.... A/D
====================2013/8/6 edit
I edit my camera class and add yaw pitch roll function
void Camera::roll(float among){
if(isChange) resetCacheAxis();
upVec=glm::rotate(upVec, among, cacheY);
isChange=true;
}
void Camera::pitch(float among){
if(isChange) resetCacheAxis();
targetVec=posVec+glm::rotate(targetVec-posVec, among, cacheX);
upVec=glm::normalize(glm::cross(cacheX, targetVec-posVec));
isChange=true;
}
void Camera::yaw(float among){
if(isChange) resetCacheAxis();
targetVec=posVec+glm::rotate(targetVec-posVec, among, cacheZ);
isChange=true;
}
void Camera::resetCacheAxis(){
cacheY=glm::normalize(targetVec-posVec);
cacheZ=glm::normalize(upVec);
cacheX=glm::cross(cacheY, cacheZ);
}
and I implement mouse camera control with yaw and pitch
void FPSCamera::rotateScreen(float dx, float dy){
yaw(-dx);
pitch(dy);
}
the problem is still there....
after moving the mouse along a circle path several times.....

The camera should rotate about 2 axis. The up vector and the strafe vector. Rotating about the up vector gives the side to side "look", while rotating about the strafe vector gives the up and down "look".
When you perform the rotation, you need to not only transform the direction, but also the up vector and strafe vector. You don't seem to be doing that here. At least 2 vectors must be changed after a transformation as all 3 vectors must be orthogonal to each other.
Take a look at the following as pseudocode:
public static void MouseLook(float x, float y)
{
Matrix xRot = Matrix.CreateFromAxisAngle(Vector3.Up, x * rotationSpeed);
Matrix yRot = Matrix.CreateFromAxisAngle(Vector3.Right, y * rotationSpeed);
viewMatrix = viewMatrix * yRot * xRot;
// Orthogonalize the matrix
OrthoNormalize();
}
private static void OrthoNormalize()
{
Vector3 zAxis = viewMatrix.Forward;
zAxis.Normalize();
Vector3 xAxis = Vector3.Cross(Vector3.Up,zAxis);
xAxis.Normalize();
Vector3 yAxis = Vector3.Cross(xAxis,zAxis);
yAxis.Normalize();
viewMatrix.Forward = zAxis;
viewMatrix.Up = yAxis;
viewMatrix.Right = xAxis;
}
Check out the following question as it has the answer to what you're looking for: Camera Rotation

Related

Problems rotating objects using eulers - quaternions

I'm having problems rotating GameObjects in my engine. I'm trying to rotate in 2 ways.
I'm using MathGeoLib to calculate maths in the engine.
First way: Rotates correctly around axis but if I want to rotate back, if I don't do it following the inverse order then rotation doesn't work properly.
e.g:
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate Y axis -50 degrees, Rotate X axis -30 degrees. Works.
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate X axis -50 degrees, Rotate Y axis -30 degrees. Doesn't.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
float3 diff = euler_rotation - editor_rotation;
editor_rotation = euler_rotation;
math::Quat mod = math::Quat::FromEulerXYZ(diff.x * DEGTORAD, diff.y * DEGTORAD, diff.z * DEGTORAD);
quat_rotation = quat_rotation * mod;
UpdateMatrix();
}
Second way: Starts rotating good around axis but after rotating some times, then it stops to rotate correctly around axis, but if I rotate it back regardless of the rotation order it works, not like the first way.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
editor_rotation = euler_rotation;
quat_rotation = math::Quat::FromEulerXYZ(euler_rotation.x * DEGTORAD, euler_rotation.y * DEGTORAD, euler_rotation.z * DEGTORAD);
UpdateMatrix();
}
Rest of code:
#define DEGTORAD 0.0174532925199432957f
void ComponentTransform::UpdateMatrix()
{
if (!this->GetGameObject()->IsParent())
{
//Get parent transform component
ComponentTransform* parent_transform = (ComponentTransform*)this->GetGameObject()->GetParent()->GetComponent(Component::CompTransform);
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//Multiply the object transform by parent transform
transform_matrix = parent_transform->transform_matrix * transform_matrix;
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
else
{
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
}
MathGeoLib:
Quat MUST_USE_RESULT Quat::FromEulerXYZ(float x, float y, float z) { return (Quat::RotateX(x) * Quat::RotateY(y) * Quat::RotateZ(z)).Normalized(); }
Quat MUST_USE_RESULT Quat::RotateX(float angle)
{
return Quat(float3(1,0,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateY(float angle)
{
return Quat(float3(0,1,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateZ(float angle)
{
return Quat(float3(0,0,1), angle);
}
Quat(const float3 &rotationAxis, float rotationAngleRadians) { SetFromAxisAngle(rotationAxis, rotationAngleRadians); }
void Quat::SetFromAxisAngle(const float3 &axis, float angle)
{
assume1(axis.IsNormalized(), axis);
assume1(MATH_NS::IsFinite(angle), angle);
float sinz, cosz;
SinCos(angle*0.5f, sinz, cosz);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}
Any help?
Thanks.
Using Euler angles and or Quaternions adds some limitations as it creates singularities which if not handled correctly will make silly things. Sadly almost all new 3D games using it wrongly. You can detect those by the well known things like:
sometimes your view get to very different angle that should not be there
object can not rotate anymore in some direction
object start rotating around different axises than it should
view jumps around singularity pole
view is spinning or flipping until you move/turn again (not the one caused by optic mouse error)
I am using cumulative transform matrices instead:
Understanding 4x4 homogenous transform matrices
Read the whole stuff (especially difference between local and global rotations) then in last 3 links you got C++ examples of how to do this (also read all 3 especially the preserving accuracy ...).
The idea is to have matrix representing your object coordinate system. And when ever you rotate (by mouse, keyboard, NAV,AI,...) you rotate the matrix (incrementally). The same goes for movement. This way they are no limitations or singularities. But also this approach has its problems:
lose of accuracy with time (read the preserving accuracy example to deal with this)
no knowledge about the Euler angles (the angles can be computed from the matrix however)
Both are solvable relatively easily.
Now when you are rotating around local axises you need to take into account that with every rotation around some axis you change the other two. So if you want to get to the original state you need to reverse order of rotations because:
rotate around x by 30deg
rotate around y by 40deg
is not the same as:
rotate around y by 40deg
rotate around x by 30deg
With cumulative matrix if you want to get back you can either iteratively drive your ship until it faces desired directions or remember original matrix and compute the rotations needed to be done one axis at a time. Or convert the matrix difference into quaternion and iterate that single rotation...

Project an object with rotation around another object in C++/OpenGL

I'm Using OpenGL/C++ to create a game.
One aspect of this game that I'm working on is having a character that shoots a projectile the way said character is facing. To do this I have a 'player' and a 'projectile'.
I pass to the projectile the characters x and y co-ordinates, the angle the player is facing. From this I want to shoot the projectile in that direction.
In my draw I am currently using glTranslate with the characters x and y and rotating the projectile on the way the character is facing. This moves my projectile to the way the player is facing.
glTranslatef(this->m_X, this->m_Y, 0);
glRotatef(angle, 0, 0, 1);
This is where i'm stuck, I can move the projectile position by incrementing/decrementing the X and Y values in the translate. But what I'm trying to ask is how can I move the projectile along the line the player is facing.
Thanks for the help!
You can use polar vectors for these calculations.
http://mathworld.wolfram.com/PolarVector.html
A polar vector will allow you to make several calculations that would normally be complicated and convoluted in a simple way. Using their applied mathematics your request won't be an issue.
Here's an implementation of mine of polar vectors.
The header file:
#include <cmath>
//Using SFML Vector2 class, making a similar class is easy.
//Check this URL: http://www.sfml-dev.org/documentation/2.3.2/classsf_1_1Vector2.php
class PolarVector
{
public:
float r;
float t; ///Angle stored in degrees.
PolarVector();
PolarVector(float radius, float angle);
PolarVector(const sf::Vector2f V2); ///Conversion constructor.
sf::Vector2f TurnToRectangular() const;
};
PolarVector TurnToPolar(const sf::Vector2f point);
float getConvertedRadius(const sf::Vector2f point);
float getConvertedAngle(sf::Vector2f point);
bool operator ==(const PolarVector& left, const PolarVector& right);
bool operator !=(const PolarVector& left, const PolarVector& right);
And the source file:
#include "PolarVector.hpp"
PolarVector::PolarVector()
:r(0.f)
,t(0.f)
{}
PolarVector::PolarVector(float radius, float angle)
:r(radius)
,t(angle)
{}
PolarVector::PolarVector(const sf::Vector2f V2)
:r(getConvertedRadius(V2))
,t(getConvertedAngle(V2))
{}
sf::Vector2f PolarVector::TurnToRectangular() const
{ return sf::Vector2f(static_cast<float>(r* std::cos(t)), static_cast<float>(r* std::sin(t))); }
PolarVector TurnToPolar(const sf::Vector2f point)
{
PolarVector PV;
PV.r = getConvertedAngle(point);
PV.t = getConvertedRadius(point);
return PV;
}
float getConvertedRadius(const sf::Vector2f point)
{ return std::sqrt((point.x * point.x) + (point.y * point.y) ); }
float getConvertedAngle(const sf::Vector2f point)
{ return std::atan2(point.y, point.x); }
bool operator ==(const PolarVector& left, const PolarVector& right)
{
float diffR = left.r - right.r;
float diffA = left.t - right.t;
return ((diffR <= EPSILON) && (diffA <= EPSILON));
}
bool operator !=(const PolarVector& left, const PolarVector& right)
{
float diffR = left.r - right.r;
float diffA = left.t - right.t;
return !((diffR <= EPSILON) && (diffA <= EPSILON));
}
The reason why I suggest this is because you can do the following.
Let's say you have a 2 dimensional vector:
sf::Vector2f character(0.f, 0.f); //Origin point. First parameter is X, second is Y
float angleCharFacesAt = 0.698132; //40 degrees in radians. C++ Trigonometry uses Radians. std::cos, std::sin and std::atan2 are used internally.
For the first object or character. You want the other object to have the same angle, but a different position.
Let's say the other object has a position above it:
sf::Vector2f object(0.f, 10.f); //Above the origin point.
float angleObjectFacesAt = 0.f; //0 degrees.
So all you need to do is rotate it using a polar vector:
PolarVector PV = TurnToPolar(object); //Use this for calculations.
PV.t += angleCharFacesAt; //t is the angle parameter of the polar vector.
object = PV.TurnToRectangular(object);
By doing this you will get the rotated position of the object.
The distance between one object and the other will always be the r (Radius) value of the polar vector. So you could make the distance longer or shorter by doing this:
PolarVector PV = TurnToPolar(object); //Use this for calculations.
PV.r += 10; //Increase the radius to increase the distance between the objects.
object = PV.TurnToRectangular(object);
You should try to understand the rotation matrix and polar math to be able to achieve more things with this, but with this code it is possible. You should also put all this code in a class, but first play with it until you understand it well.
Sorry for the lengthy answer, but this is a topic that isn't very easy to explain without delving into linear algebra. The classes are for actual code manageability (I use these in my own game), but you can reproduce the same effects with the calculations only.
I personally prefer Polar Vectors over using the rotation matrix due to their usefulness in more than just rotating an object. But here's a link to understanding the rotation matrix better: https://en.wikipedia.org/wiki/Rotation_matrix
After you've done the transformation with the polar vector, you can just glTranslate to the final position given by the polar vector. You have to make sure that you rotate around the origin you are using. Otherwise rotation might not occur as you desire to use it.

First Person Camera movement issues

I'm implementing a first person camera using the GLM library that provides me with some useful functions that calculate perspective and 'lookAt' matrices. I'm also using OpenGL but that shouldn't make a difference in this code.
Basically, what I'm experiencing is that I can look around, much like in a regular FPS, and move around. But the movement is constrained to the three axes in a way that if I rotate the camera, I would still move in the same direction as if I had not rotated it... Let me illustrate (in 2D, to simplify things).
In this image, you can see four camera positions.
Those marked with a one are before movement, those marked with a two are after movement.
The red triangles represent a camera that is oriented straight forward along the z axis. The blue triangles represent a camera that hasbeen rotated to look backward along the x axis (to the left).
When I press the 'forward movement key', the camera moves forward along the z axis in both cases, disregarding the camera orientation.
What I want is a more FPS-like behaviour, where pressing forward moves me in the direction the camera is facing. I thought that with the arguments I pass to glm::lookAt, this would be achieved. Apparently not.
What is wrong with my calculations?
// Calculate the camera's orientation
float angleHori = -mMouseSpeed * Mouse::x; // Note that (0, 0) is the center of the screen
float angleVert = -mMouseSpeed * Mouse::y;
glm::vec3 dir(
cos(angleVert) * sin(angleHori),
sin(angleVert),
cos(angleVert) * cos(angleHori)
);
glm::vec3 right(
sin(angleHori - M_PI / 2.0f),
0.0f,
cos(angleHori - M_PI / 2.0f)
);
glm::vec3 up = glm::cross(right, dir);
// Calculate projection and view matrix
glm::mat4 projMatrix = glm::perspective(mFOV, mViewPortSizeX / (float)mViewPortSizeY, mZNear, mZFar);
glm::mat4 viewMatrix = glm::lookAt(mPosition, mPosition + dir, up);
gluLookAt takes 3 parameters: eye, centre and up. The first two are positions while the last is a vector. If you're planning on using this function it's better that you maintain only these three parameters consistently.
Coming to the issue with the calculation. I see that the position variable is unchanged throughout the code. All that changes is the look at point I.e. centre only. The right thing to do is to first do position += dir, which will move the camera (position) along the direction pointed to by dir. Now to update the centre, the second parameter can be left as-is: position + dir; this will work since the position was already updated to the new position and from there we've a point farther in dir direction to look at.
The issue was actually in another method. When moving the camera, I needed to do this:
void Camera::moveX(char s)
{
mPosition += s * mSpeed * mRight;
}
void Camera::moveY(char s)
{
mPosition += s * mSpeed * mUp;
}
void Camera::moveZ(chars)
{
mPosition += s * mSpeed * mDirection;
}
To make the camera move across the correct axes.

Quaternion-Based-Camera unwanted roll

I'm trying to implement a quaternion-based camera, but when moving around the X and Y axis, the camera produces an unwanted roll on the Z axis. I want to be able to look around freely on all axis.
I've read other topics about this problem (for example: http://www.flipcode.com/forums/thread/6525 ), but I'm not getting what is meant by "Fix this by continuously rebuilding the rotation matrix by rotating around the WORLD axis, i.e around <1,0,0>, <0,1,0>, <0,0,1>, not your local coordinates, whatever they might be."
//Camera.hpp
glm::quat rotation;
//Camera.cpp
void Camera::rotate(glm::vec3 vec)
{
glm::quat paramQuat = glm::quat(vec);
rotation = paramQuat * rotation;
}
I call the rotate function like this:
cam->rotate(glm::vec3(0, 0.5, 0));
This must have to do with local/world coordinates, right? I'm just not getting it, since I thought quaternions are always based on each other (thus a quaternion can't be in "world" or "local" space?).
Also, should i use more than one quaternion for a camera?
As far as I understand it, and from looking at the code you provided, what they mean is that you shouldn't store and apply the rotation incrementally by applying rotate on the rotation quat all the time, but instead keeping track of two quaternions for each axis (X and Y in world space) and calculating the rotation vector as the product of those.
[edit: some added (pseudo)code]
// Camera.cpp
void Camera::SetRotation(glm::quat value)
{
rotation = value;
}
// controller.cpp --> probably a place where you'd want to translate user input and store your rotational state
xAngle += deltaX;
yAngle += deltaY;
glm::quat rotationX = QuatAxisAngle(X_AXIS, xAngle);
glm::quat rotationY = QuatAxisAngle(Y_AXIS, yAngle);
camera.SetRotation(rotationX * rotationY);

How to rotate an object according to it's orientation

Similar question for WebGL: Rotate object around world axis .
I need to rotate an object in a way that the user should be able to move it with the mouse, like if he's dragging it. The problem is that glRotatef just rotates the object without taking account of it's orientation. For WebGL the solution was to use quaternions, but I guess that there aren't quaternions in OpenGL.
This is how I achieve a rotation for now:
// rotation 2D GLfloat C-vector, posX, posY GLfloat's
void mouse(int button, int state, int x, int y) {
if(button== GLUT_LEFT_BUTTON) {
if(state== GLUT_DOWN)
{
posX=x;
posY= y;
onEvent= true;
}
else if(state== GLUT_UP)
{
GLfloat dx= x-posX;
GLfloat dy= y-posY;
rotation[0]= -dy/5;
rotation[1]= dx/5;
onEvent= false;
glutPostRedisplay();
}
}
}
Then I handle the rotation in the display function:
glPushMatrix();
glRotatef(rotation[0],1,0,0);
glRotatef(rotation[1],0,1,0);
// Draw the object
glPopMatrix();
It kinda works, but like I said it should like if the user is able to drag the object to rotate it. Instead if for example the object is rotated of 90 degrees around the X axis, when the user drags the mouse horizontally to make it rotate around the Y axis, it rotates in the inverse direction. I need an idea here, how could I do that?
Edit
I tried to use glMultMatrixf, but the object doesn't rotate correctly: it gets scaled instead of rotating, this is the code I've edited in the mouse function:
// Global variables:
// GLfloat xRotationMatrix[4][4];
// GLfloat yRotationMatrix[4][4];
else if(state== GLUT_UP && onEvent)
{
GLfloat dx= (x-posX)/(180.0*5)*2.0*M_PI;
GLfloat dy= (y-posY)/(180.0*5)*2.0*M_PI;
// Computing rotations
double cosX= cos(dx);
double sinX= sin(dy);
double cosY= cos(dy);
double sinY= sin(dy);
// x axis rotation
xRotationMatrix[1][1]+= cosY;
xRotationMatrix[1][2]+=-sinY;
xRotationMatrix[2][2]+= sinY;
xRotationMatrix[2][2]+= cosY;
// y axis rotation
yRotationMatrix[0][0]+= cosX;
yRotationMatrix[0][2]+= sinX;
yRotationMatrix[2][0]+= -sinX;
yRotationMatrix[2][2]+= cosX;
onEvent= false;
glutPostRedisplay();
}
Then in the display function:
glPushMatrix();
glMultMatrixf((const GLfloat*)xRotationMatrix);
glMultMatrixf((const GLfloat*)yRotationMatrix);
glutSolidTeapot(10);
glPopMatrix();
This is the non rotated teapot:
If I drag the mouse horizontally to rotate the teapot around the y axis, instead of the rotation this is what I get:
First of all a bit of algebra.
Let v be a vector, M your current modelview matrix, and R the matrix associated with a glRotate command. Then, if you use glRotate, what you get is:
M * R * v
That means you are rotating around object axes. You want to rotate around the world axes, that is:
R * M * v
See the difference? Unfortunately GL doesn't have a MatrixPreMult function.
In modern OpenGL we don't use the matrix stack anymore, in fact while working with shaders we manually pass the transformation matrices to the GL program. What (most) people do is write/use an external vector algebra library (like Eigen).
One possible (untested) workaround which uses only the old deprecated GL stuffs may be something like this:
void rotate(float dx, float dy)
{
//assuming that GL_MATRIX_MODE is GL_MODELVIEW
float oldMatrix[4][4];
glGetFloatv(GL_MODELVIEW_MATRIX,oldMatrix);
glLoadIdentity();
glRotatef(-dy,1,0,0);
glRotatef(dx,0,1,0);
glMultMatrixf(oldMatrix);
}
And you put this code in your mouse function, not in the draw routine.
You can use this trick by keeping the view matrix in the GL matrix stack, then pushing/popping everytime you have to draw an object. I wouldn't recommend something like that in a large project.
Notice also that if you invert the order of the two glRotate calls in the code above you can get slightly different results, expecially if dx and dy are not small.
This code might be slightly better:
float angle = sqrt(dx*dx+dy*dy)*scalingFactor;
glRotate(angle,-dy,dx,0);