I'm trying to implement the gimbal lock fix described in this question, but I end up getting weird behavior where when I rotate on the X axis (or try to at least) it rotates on both the X and Y axis at the same time, but when i try and rotate on the Y axis nothing happens at all.
I'm keeping total orientation in a quaternion and (trying to) update it with a rotation matrix constructed by forward, right, and up vectors. I'm deriving these vectors like so:
// From https://learnopengl.com/Getting-started/Camera
_forward.X = (float) (MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.Y)) *
MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.X)));
_forward.Y = (float) MathHelper.Sin(MathHelper.DegreesToRadians(_rotation.X));
_forward.Z = (float) (MathHelper.Sin(MathHelper.DegreesToRadians(_rotation.Y)) *
MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.X)));
_forward.Normalize();
_right = Vector3.Cross(_forward, _worldUp).Normalized();
_up = Vector3.Cross(_right, _forward)
and updating the _rotation values like this:
public void RotateX(float rotation) {
_rotation.X += rotation * _sens;
}
public void RotateY(float rotation) {
_rotation.Y += rotation * _sens;
}
To get the rotation matrix I'm just putting the 3 vectors into a Matrix3 object:
var r1 = new Vector3(_right.X, _up.X, _forward.X);
var r2 = new Vector3(_right.Y, _up.Y, _forward.Y);
var r3 = new Vector3(_right.Z, _up.Z, _forward.Z);
var mat = new Matrix3(r1, r2, r3);
if (mat != _check) {
_orientation = Quaternion.FromMatrix(mat) * _orientation;
}
_orientation.Normalize();
and then I update the orientation quaternion.
The _check
property is just a matrix that represents what should be created if there is no change in rotation so _orientation
isn't changed when it doesn't have to be.
This is a gif of what happens when I just use the RotateX function, so it just rotates on some weird axis. Any clue what I'm doing wrong? Am I initializing the rotation matrix wrong or is it something else entirely?
[edit]
Camera pitch now works without affecting Yaw and Roll:
public void Update() {
_forward.X = (float) (MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.Y)) *
MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.X)));
_forward.Y = (float) MathHelper.Sin(MathHelper.DegreesToRadians(_rotation.X));
_forward.Z = (float) (MathHelper.Sin(MathHelper.DegreesToRadians(_rotation.Y)) *
MathHelper.Cos(MathHelper.DegreesToRadians(_rotation.X)));
// _forward.Normalize();
_right = Vector3.Cross(_forward.Normalized(), _worldUp);
_up = Vector3.Cross(_right.Normalized(), _forward.Normalized());
Console.Out.WriteLine("forward: {0}", _forward);
Console.Out.WriteLine("right: {0}", _right);
Console.Out.WriteLine("up: {0}", _up);
var r1 = new Vector3(_right.X, _up.X * _worldUp.X, _forward.X);
var r2 = new Vector3(_right.Y, _up.Y * _worldUp.Y, _forward.Y);
var r3 = new Vector3(_right.Z, _up.Z * _worldUp.Z, _forward.Z);
var mat = new Matrix3(r1, r2, r3);
if (mat != _check) { // Change in orientation.
_orientation = Quaternion.FromMatrix(mat) * _orientation;
Console.Out.WriteLine(_orientation.ToEulerAngles());
}
_orientation.Normalize();
Console.Out.WriteLine("Position: {0}", _position);
_rotation = Vector3.Zero;
}
But no rotation occurs when trying to rotate about Y axis.
r1
,r2
, andr3
values? Also, you can skip the matrix step entirely by just computing a pitch quaternion (x: sin(pitch/2), w: cos(pitch/2)
) and a yaw quaternion (y:sin(yaw/2), w:cos(yaw/2)
) and then multiplying them. \$\endgroup\$