I\'m having a big problem limiting the camera pitch angle (between -90º and 90º) with the code below. This is somewhat a follow up to this question.
The problem, it
Problem 1 : The code belew "// Makes sure all vectors are perpendicular all the time" makes sure that your UP camera vector is actually up (with respect to WORLD_SKY_VECTOR). So :
Look down at your chest. The top of your head is actually down. If you want to look at the same thing, from the same point, but with the top of your head up, you'd have to rotate it (not humanly feasible, sadly)
Problem 2 : Same as above. CrossProduct(Reference, WORLD_SKY_VECTOR) gives cross(up,down) which does not mean anything, just try on paper.
If you want to be able to look "upside down" :
So :
yaw += whatever;
pitch += whatever;
FrontVector = SphericalToCartesian(yaw, pitch);
RightVector = SphericalToCartesian(yaw + PI/2.0f, 0.0);
UpVector = cross(RightVector, FrontVector);
with SphericalToCartesian beeing something like if (pitch=0,yaw=0) means looking to south:
x = cos(pitch) * sin(yaw)
y = sin(pitch)
z = cos(pitch) * cos(yaw)
After while I come up with a solution which is pretty simple and straightforward. Like it was said many times in the answers and comments, the problem lies when the forward vector (my Reference
) is looking straight up or down, which means this vector is parallel to WORLD_SKY_VECTOR
and that's reason it gets all messy.
My thought behind this solution is that when I'm looking straight down (or up) and I want to rotate left or right, this is actually a roll rotation around the forward vector. Why not execute a roll movement when the pitch angle is at -90º or 90º instead?
Putting that together, I solved the problem by simply replacing the yaw/heading rotation with the following code:
if(angle.y != 0.0f) {
if(abs(accumPitchAngle) == 90.0f) {
RightVector = RotateArbitraryAxis(RightVector, Reference, -angle.y);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
} else {
Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
}
}
This seems a fairly simple and straightforward working solution, I'll probably be marking my answer as accepted unless someone points out any strong problem against this implementation that I may not be considering. I'll wait a few hours before doing it, so that the previous answers reach some sort of conclusion.
You can't take the cross product of two parallel vectors. I think that's where it's failing, i.e. when accumPitchAngle is ±90°.
You might want to limit it to -89.999° ~ +89.999°.
Edit: To start from the beginning, you want to convert pitch & yaw into a forward vector and an up vector for gluLookAt()
, right? Then I suggest:
1) Use yaw
(and yaw
only) to create a right
vector that is parallel to the ground.
2) Cross right
and WORLD_SKY_VECTOR
to get a forward
vector that is correct in yaw but not in pitch.
3) Rotate forward
around right
through pitch
degrees to get a forward vector that is correct in both pitch and yaw. (I think you've got this far.)
4) Cross forward
and right
to get a camera up
vector that will work in all cases.