-->

main-navbar

Saturday, July 6, 2013

A word on Quaternions and doing complex rotations

Its been some time since I last made a post here. Not for lack of working, but rather of lack of something to post. For the past several months I have been working quite a bit in Unity3d on Quaternions.

I will talk about that below; the first point is that I haven't made many posts here. To keep whoever bothers following this blog on my project interested and up to date, I will write a blog post (to the best of my ability; post is still not guaranteed) every Saturday with some info on what has been worked on/ updated etc. And for those that even that is not enough, I plan on making a twitter account with daily updates on progress. Now as a warning to those who wish to read on, below is a rant/very long post on quaternions which some may find fascinating and others boring.

Quaternions are what unity uses to describe rotations. Unity also has something called eulerangles, which  has the more easily interpreted 0-360 notation that we are used to using for rotations. The main reason unity uses quaternions as the underlying way of calculating an objects rotation though, is because quaternions don't suffer from something called gimbal lock. The major disadvantage of quaternions, is that their representation is very complex, so much so in fact that quaternions are best simply referred to as variables instead of their individual values. For a mathematical explanation of quaternions, here is a good explanation that might give you a little bit of an idea of what the values actually mean. However, the explanation went a bit over my head, and the variable method of representation is sufficient.

My problem with quaternions started when I tried to do something a bit complex, and all at once. The idea was to add a hook function for rotating one side of a module to be facing another side of another module.  So I setup something simple for selecting the sides that needed to face each other. I set it up so all that was needed was the new rotation to rotate too.

Each object stores its side as a rotation and position relative too the center of the module it is on. This greatly reduces lag from object creation and destruction compared to when I had an entire GameObject representing each side, but it added a complication; instead of rotating one module to be facing another, I had to rotate one rotation of a modules side to be facing another rotation of another modules side.

In the below example side one is selected to rotate facing side 3, and the goal was to rotate it so it looked like side 2; both sides plains are parallel, and the corners of the modules are aligned.
My first solution to get a rotation that would rotate in this way was to try rotating all at once, and i came up with this line of code:  

gameObject.transform.rotation*Quaternion.Euler(orbit))*(Quaternion.Inverse(Quaternion.Euler(side.orbit+Vector3(0, 0, 180)))
note: orbit is the sides relative rotation in euler angles to the module it is on. Quaternion.Euler converts it to a quaternion.

This line is the result from hours of trial and error and was by no means the most efficient time I spent. However, it still worked to provide the desired rotation. I am honestly still not entirely sure why it works, but it does, so I was fine with it. That is, until I tried to rotate the resulting rotation. 

At some point I realized I needed to rotate the returned rotation, the reason being that if you connect modules into a "loop" where there are connections connecting in a loop pattern, the assembly will destroy itself. In the example below the red shows the "loop" that would be created. The blue shows the corner of the side that would rotate toward the other corner of the other triangle if the above line of code was used to provide the target rotation.
If a joint is added with this rotation as the target, then the assembly literally flies apart. So as a solution I attempted to use the angle axis function to rotate the resulting rotation in a way that would return the correct target rotation. Only problem was, using the angle axis function seriously messed up the rotation; it rotated on some arbitrary axis as a result of the original rotation calculations. I spent a long time attempting to rectify this, but nothing worked and I was forced to attempt a different approach.

I was back to square one and I did what I should have done from the start: rotate in steps. Eventually I came up with a rotation that would align the faces of the two objects, which was really a modified LookAt function. From there I was able to use angle axis to rotate the previous rotation to the final rotation. This resulted in the desired rotation of the modules.

To sum up: Do quaternion rotations in steps; it will save you a LOT of trouble, and will certainly not cause you to learn a million things about quaternions you never needed to know.
note: the last part of the above sentence is not guaranteed not to occur. 

Edit: The details (with the actual code) on how this was solved can be found here.





2 comments:

  1. Nice posting.
    Could you post the Final Solution of how this was solved. Thanks.

    ReplyDelete
    Replies
    1. Thanks for the suggestion! I'll have to make another blog post, while the solution is simpler then I thought, it is still a good deal complex enough to deserve its own post.

      Delete