Reputation: 165
As I spin my mouse CW the objects on the screen rotate CCW, and vice versa. It's not a huge bug but it seems wrong.
I'm using Eigen for quaternions and vectors so the issue should just fall under the code I have posted.
Camera:
#include <Eigen/Dense>
#include <cstring>
#include <math.h>
typedef Eigen::Vector3f vec3;
typedef Eigen::Quaternionf Quaternion;
namespace vec
{
//
// Constants
//
const vec3 i(1, 0, 0);
const vec3 j(0, 1, 0);
const vec3 k(0, 0, 1);
const vec3 zero(0, 0, 0);
const vec3 ones(1, 1, 1);
}
class Camera
{
void init();
void moveX(float dist);
void moveY(float dist);
void moveZ(float dist);
void rotateX(float radians); //yaw
void rotateY(float radians); //pitch
void rotateZ(float radians); //roll
void moveLeft(float dist);
void moveForward(float dist);
void moveUp(float dist);
void rotateLeft(float radians); //yaw
void rotateUp(float radians); //pitch
void rollLeft(float radians); //roll
void setPosition(const vec3 &pos);
void setDirection(const vec3 &dir);
void lookAt(const vec3 &pos); //changes direction
// Camera Vectors
vec3 left() const;
vec3 right() const;
vec3 up() const;
vec3 down() const;
vec3 forward() const;
vec3 backward() const;
const vec3 &pos() const;
protected:
Quaternion mRotation;
vec3 mPos;
void rotateL(float radians, const vec3 &axis);
void rotateR(float radians, const vec3 &axis);
};
Implementation:
void Camera::init()
{
mPos.setZero();
mRotation.setIdentity();
}
inline void Camera::rotateL(float radians, const vec3 &axis)
{
Quaternion q(Eigen::AngleAxis<float>(radians, axis));
mRotation = (q * mRotation).normalized();
}
inline void Camera::rotateR(float radians, const vec3 &axis)
{
Quaternion q(Eigen::AngleAxis<float>(radians, axis));
mRotation = (mRotation * q).normalized();
}
void Camera::moveX(float dist)
{
mPos.x() += dist;
}
void Camera::moveY(float dist)
{
mPos.y() += dist;
}
void Camera::moveZ(float dist)
{
mPos.z() += dist;
}
void Camera::rotateX(float radians)
{
rotateL(radians, vec::i);
}
void Camera::rotateY(float radians)
{
rotateL(radians, vec::j);
}
void Camera::rotateZ(float radians)
{
rotateL(radians, vec::k);
}
void Camera::moveLeft(float dist)
{
mPos += dist * left();
}
void Camera::moveUp(float dist)
{
mPos += dist * up();
}
void Camera::moveForward(float dist)
{
mPos += dist * forward();
}
void Camera::rotateLeft(float radians)
{
rotateL(radians, up());
}
void Camera::rotateUp(float radians)
{
rotateL(radians, left());
}
void Camera::rollLeft(float radians)
{
rotateL(radians, forward());
}
void Camera::setPosition(const vec3 &pos)
{
mPos = pos;
}
void Camera::setDirection(const vec3 &dir)
{
mRotation.setFromTwoVectors(vec::k, dir);
mRotation.normalize();
}
void Camera::lookAt(const vec3 &pos)
{
setDirection(mPos - pos);
}
// Camera Vectors
vec3 Camera::left() const
{
return mRotation._transformVector(vec::i);
}
vec3 Camera::right() const
{
return -left();
}
vec3 Camera::up() const
{
return mRotation._transformVector(vec::j);
}
vec3 Camera::down() const
{
return -up();
}
vec3 Camera::forward() const
{
return mRotation._transformVector(vec::k);
}
vec3 Camera::backward() const
{
return -forward();
}
const vec3 &Camera::pos() const
{
return mPos;
}
Updates Functions:
void App::onMouseMotion(int x, int y, int dx, int dy)
{
cam.rotateUp(dy * ROTATE_SCALE);
cam.rotateLeft(-dx * ROTATE_SCALE);
}
void App::update()
{
cam.moveLeft(((int)Keyboard::isKeyDown('a')) * MOVE_SCALE * mDt);
cam.moveLeft(((int)Keyboard::isKeyDown('d')) * -MOVE_SCALE * mDt);
cam.moveForward(((int)Keyboard::isKeyDown('w')) * MOVE_SCALE * mDt);
cam.moveForward(((int)Keyboard::isKeyDown('s')) * -MOVE_SCALE * mDt);
cam.rollLeft(((int)Keyboard::isKeyDown('q')) * -ROLL_SCALE * mDt);
cam.rollLeft(((int)Keyboard::isKeyDown('e')) * ROLL_SCALE * mDt);
}
I've read that some issues are due to combining Quaternions on the incorrect side... such as QR instead of RQ. Switching the X axis as suggested here definitely doesn't help.
I'd also appreciate any suggestions with my implementation. I've consider switching to matrices for rotations but normalizing and combining them is more expensive.
Upvotes: 1
Views: 508
Reputation: 89221
That is a normal behavior when you are doing repeated application of rotations. You can see it in some applications or games that allow free rotation.
What you see is that the hand is now facing about the same direction, but is rolled slightly counter-clockwise.
This might seem strange to humans, since we are used to a fixed up-direction.
Upvotes: 2