You're absolutely right. I just finished adapting the code and it still suffers from gimbal lock. I've read your post several times and sadly don't feel I quite understand what you're advising.
On the one hand, I completely understand (well, obviously not completely ) why the euler angles are still problematic. I was wary when I began as it seemed like I was basically utilizing quaternions to represent the euler angles. Even being as weak in mathematics as I am, I can kind of understand why this isn't working.
I've tried several methods, but ended up using a pretty straight-forward conversion of Angles to quaternions.
Embarrassingly (mostly because in hindsight, I'm soooo doing this wrong), here is a simplified version of what I was doing:
glm::vec3 eulerAngles(angleInY, angleInX, angleInZ);
quateyMcQuaternion = glm::quat(eulerAngles);
and then multiplying that against my view matrix. It works basically like you'd think it would. Essentially as though I were just using Euler angles.
The key to avoiding gimbal lock isn't using magical fairy dus.. quaternions, but to stop using Euler angles. Do not store angles, period. Any order of rotation is lost if you just wildly sum up angles. When something rotates, apply the rotation directly to the matrix or quaternion. When using matrices, multiply either from the left or right, depending on whether you rotate around a global or local axis.
If you have the time and don't mind elaborating, I'm not quite following you. If you don't mind, I'm going to break down where I'm struggling here.
"stop using Euler angles. "
I would love to stop using Euler Angles. Where I struggle is figuring out how to rotate from point A to point B without calculating that distance via angles. Unless you mean that it's O.K to calculate that as long as you do the appropriate things afterward (if this is the case, I guess what I need to know is the process after that).
"Do not store angles. Any order of rotation is lost if you just wildly sum up angles. When something rotates, apply the rotation directly to the matrix or quaternion."
I think I'm missing your point here. At render time, I'm multiplying the ViewMatrix by the quaternion. Well, I'm converting the quaternion to a matrix first and then multiplying (plus some translating and such, depending on the object). If I don't store the angle then it reverts back to the default view since no angles are being fed into the quaternion. In hindsight, I can try playing with this idea as I think I see what you're getting at, maybe (in the meantime I'll work on this and see what happens). I may be thinking about this wrong, but if I set my view matrix to the new position and reset all the angles, instead of rotating by a large angle at each draw, I can rotate by a series of small ones and maybe not have the same issues(?). Right now the View Matrix isn't changing each draw cycle, it stays the same and only the rotation it's multiplied by is changed.
And, I understand the order of multiplication, I'm fairly certain.
So, after reading kunos comment, I've perhaps realized I wasn't understanding quaternions at all. So, I'm heading back to the drawing board to work things out. Feel free to ignore all the above questions unless they're still pertinent and just call me an idiot
Anyhow, whether or not you have the time to respond, I appreciate your input and patience. I'm really not quite as dense as it may seem here, I'm just really new to opengl and in a little over my head.
"So if you want to "pitch" your spaceship, do not change the "pitch angle".. create a rotation matrix around the X axis and multiply that by your current matrix.. you have now pitched your ship."
This actually makes a little sense to me, for once. I'll definitely need to go back and figure a lot of things out. But, a quaternion is just an axis plus rotation as the W value, correct? Oddly enough, unless I'm still completely misunderstanding things still, this kind of sheds a lot of light that numerous tutorials haven't done for me. To use a bad analogy, it's like having a flashlight tied perpendicularly to a bar(the flashlight just representing the view) and holding the bar out in front of you, but instead of turning the bar left and right, you're rolling it in your hands. And then of course your have two more bars at the other axes? If this is the case i have been thinking about this all wrong (I've been thinking about rotating axes, rather than rotating along axes, which is totally different and didn't exactly make sense until this moment).
Basically, to look up and down, I can use a quaternion with the x axis as the x,y,z(since the view is looking down the z) with the w being the rotation, and when multiplied by the view matrix it should rotate the view up and down. To look left/right, I'd use the Y axis, and Z for a roll type rotation. I'll totally have to hit the drawing board again, but for the first time since starting this,I think a lightbulb just went off in my head >.<. Eugghhluckh, now I have to figure out how to find my axes. Are they always like 1,0,0 or what have you since the view is always rotated(I'm confusing world and local space here, but the view is always set to 0,0,0 looking down the z axis, right?), or does this need to be recalculated each time since it's not offset?
And lastly, is doing multiple rotations costly(i.e if you're pressing up and left, does multiplying the view by up then left give bad results) or should I be trying to calculate a different axis to represent the two directions? The former would certainly be simpler, but I'm happy to bang out the math behind the latter if it's needed. Obviously, two is more than one But, if it's a small calculation I'm not above a little unnecessary code
**Last edit, I promise.
Alright, I've implemented the advice both of you have given and it seems to be working(at least along a single axis ). I had a momentary setback initially because I wasn't clearing the angle each cycle(like triento suggested I should), but restructuring the code a bit solved that , but introduced a weird flickering, that after some troubleshooting I've sorted that out. In hindsight, using multiple quaternions to handle multiple axes seems way unnecessary. So, now all I have to do is change the controls so they alter the rotation axis so I can rotate towards different axes and I should be all set!
I just wanted to give one last thank you to you both for your advice. Quaternions don't seem to be nearly as un-intuitive as I was led to believe, nor as difficult to implement. Just, all the tutorials I came across seemed to do a poor job of actually explaining the fundamental idea of what was going on (or, they did a great job but hadn't dumbed it down to my level).
Anyhow, all seems to be in working order, feel free to add any advice I may have overlooked thus far, but thanks again, and cheers
**real last edit.
Eh, after implementing controls to change the axis, I now have "up" issues. But, I'll try to sort that out on my own.
Actually, "up" seems to be fine. I'm somehow not taking into account what I've already rotated. So, if I say, look to my left 90 degrees, then look up, I'm rotating the world as though I were still looking from my original position, but since I'm not, the rotation looks wrong. I'll suss it out eventually, but if you have any ideas what I'm doing wrong there, I'm all ears
***seriously, I won't edit this again >.<
Alright, everything is sorted out, the funny behavior above was fixed by multiplying the view matrix by the rotation and the view matrix itself. So, everything is fine and dandy and working wonderfully. I'm rotating in all directions without any gimbal lock. I still have to figure out how all that fits in with velocity and position and such, but that I won't update here as it's entirely another subject and I should be able to work all that out. My genuine gratitude to you both as you as your help led me to actually understand what quaternions actually were. Also, my apologies for typing out the whole messy process here. I figure at the very least it may help someone else learn from my sad mistakes
Edited by Misantes, 08 June 2014 - 04:51 PM.