Hey GameDev!
I'm having some trouble figuring out how yawing and pitching my camera in 3d works and I was wondering if anyone could help me out. Over the last two, three days I've been trying out all sorts of matrix multiplications and stuff, but I can't seem to find the right order of multiplification. Forgive me for posting these elaborate pieces of code, but I thought it to be better than having to explain this to you without examples. This is what I currently have:
A Frame class. Represents an orientation and location in 3d space:
class Frame
{
public:
Vector3f vLocation;
Vector3f vForward;
Vector3f vUp;
Frame();
Matrix44f GetMatrix();
void Move(const float fX, const float fY, const float fZ);
void MoveX(const float fDelta);
void MoveY(const float fDelta);
void MoveZ(const float fDelta);
void Rotate(float fYaw, float fPitch, float fRoll);
void Yaw(float fAngle);
void Pitch(float fAngle);
void Roll(float fAngle);
};
A Camera class. Derived from Frame. First I used Frame for the camera orientation, but I got to the conclusion I had to store the yaw, pitch and roll in seperate variables, and then rebuild that camera orientation each frame. Could anyone tell me if this is correct?
class Camera : public Frame
{
public:
float fYaw, fPitch, fRoll;
Camera();
void Yaw(float fAngle);
void Pitch(float fAngle);
void Roll(float fAngle);
void Compute(); // Computes the orientation
};
Then, when the mouse moves horizontal or vertical, I give calls to respectively Camera::Yaw() and Camera::Pitch(), functions that both add fAngle to the corresponding member. Nothing more. When the rendering starts, I give a call to Camera::Compute():
void Camera::Compute()
{
// Reset the orientation
vForward.x = 0.0f;
vForward.y = 0.0f;
vForward.z = -1.0f;
vUp.x = 0.0f;
vUp.y = 1.0f;
vUp.z = 0.0f;
// AND THIS IS WHERE I THINK THINGS GO WRONG.
Frame::Yaw(fYaw);
Frame::Pitch(fPitch);
}
// More functions down there \/
void Frame::Yaw(float fAngle)
{
// Switch to radians
fAngle /= DEG_TO_RAD;
// Calculate the rotation matrix
Matrix44f m = MatrixRotation(fAngle, vUp.x, vUp.y, vUp.z);
// Rotate the Forward vector
vForward.x = m(0, 0) * vForward.x + m(1, 0) * vForward.y + m(2, 0) * vForward.z;
vForward.y = m(0, 1) * vForward.x + m(1, 1) * vForward.y + m(2, 1) * vForward.z;
vForward.z = m(0, 2) * vForward.x + m(1, 2) * vForward.y + m(2, 2) * vForward.z;
vForward.Normalize();
}
void Frame::Pitch(float fAngle)
{
// Calculate the right vector
Vector3f vRight = CrossProduct(vUp, vForward);
// Convert the angle to radians
fAngle /= DEG_TO_RAD;
Matrix44f m = MatrixRotation(fAngle, vRight.x, vRight.y, vRight.z);
vForward.x = m.f[0] * vForward.x + m.f[4] * vForward.y + m.f[8] * vForward.z;
vForward.y = m.f[1] * vForward.x + m.f[5] * vForward.y + m.f[9] * vForward.z;
vForward.z = m.f[2] * vForward.x + m.f[6] * vForward.y + m.f[10] * vForward.z;
vForward.Normalize();
vUp.x = m.f[0] * vUp.x + m.f[4] * vUp.y + m.f[8] * vUp.z;
vUp.y = m.f[1] * vUp.x + m.f[5] * vUp.y + m.f[9] * vUp.z;
vUp.z = m.f[2] * vUp.x + m.f[6] * vUp.y + m.f[10] * vUp.z;
vUp.Normalize();
}
inline Matrix44f MatrixRotation(float fAngle, float x, float y, float z)
{
// Calculate the sinus and cosinus of the angle
float s = sin(fAngle);
float c = cos(fAngle);
float onemc = 1 - c;
// Create the matrix
Matrix44f n;
// Fill in the data - GOT THIS CODE FROM THE INTERNET, CAN'T REMEMBER SOURCE
n.f[0] = x*x + (1 - x*x) * c;
n.f[1] = x*y * onemc + z*s;
n.f[2] = x*z * onemc - y*s;
n.f[3] = 0.0f;
n.f[4] = y*x * onemc - z*s;
n.f[5] = y*y + (1 - y*y) * c;
n.f[6] = y*z * onemc + x*s;
n.f[7] = 0.0f;
n.f[8] = z*x * onemc + y*s;
n.f[9] = z*y * onemc - x*s;
n.f[10] = z*z + (1 - z*z) * c;
n.f[11] = 0.0f;
n.f[12] = 0.0f;
n.f[13] = 0.0f;
n.f[14] = 0.0f;
n.f[15] = 1.0f;
// Return the matrix
return n;
}
Frame::Yaw() and Frame::Pitch() are both computed in local space, but since the forward and up vector are set back to initial values, Yaw is calculated as if global. Then, after we've turned horizontally, we can turn vertically from that position; pitching. At least, that's my train of thought, but apparently I'm doing something wrong.
Then, at last, this function is called with the camera class as argument:
void GraphicsUnit::ApplyFrame(Frame& Frame)
{
gluLookAt(Frame.vLocation.x, Frame.vLocation.y, Frame.vLocation.z,
Frame.vLocation.x + Frame.vForward.x, Frame.vLocation.y + Frame.vForward.y, Frame.vLocation.z + Frame.vForward.z,
Frame.vUp.x, Frame.vUp.y, Frame.vUp.z);
}
Oh, and of course,
the actual problem is that, yawing and pitching seperately work fine, but when I combine these two, the orientation starts doing weird things, as if I'm also rolling or something like that. I'm sure this kind of question has been asked before, but I can't seem to get it work with the information I could find on the internet.
Thanks for helping out,
- Stijn Frishert
[Edited by - stenny on February 28, 2009 5:36:46 AM]
What do I expect? A young man's quest to defeat an evil sorceror while discovering the truth of his origins. A plucky youngster attended by her brutish guardian. A powerful artifact which has been broken into a small number of artifactlets distributed around the world.What do I want? Fewer damn cliches. - Sneftel