# How to implement 6 Degrees Of Freedom

This topic is 2648 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I found this old post that explains the idea on how to do it but im stuck.
(check the second last post, by wojjyy)
http://www.gamedev.net/community/forums/topic.asp?topic_id=491391

as he describes in step 2 and 3:
2. get 3 variables like yawChange, pitchChange, rollChange. each frame get user input there
3. build quaternion rotationChange from these.

How do I get the pitch yaw and roll into the quaternion exactly.
My previous attemps failed me, I was doing something like
---------
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, Vector3.left);
Quaternion zQuaternion = Quaternion.AngleAxis(zRotationValue, Vector3.forward);
wantedRotation = xQuaternion * yQuaternion * zQuaternion;
transform.rotation = wantedRotation * transform.rotation
---------

But that was giving me the exact same errors as he described in the post that only 2 axis can be rotated at the same time, if a third one gets in, all the rotations get weird.

I am using unity3d to make this game, and I already have all the quaternions and everything else so I just need to know how to use them.

Unity scripting reference for all the functions can be found here:
http://unity3d.com/support/documentation/ScriptReference/index.html

like this one:
http://unity3d.com/support/documentation/ScriptReference/index.html

My game is a space shooter 6DOF is the way to go.

Part of my code to control the ship is here:
http://pastebin.com/xwkB0BXZ

-transform is unitys main object that inherits all the main functions and has the meshes attached to.
-Input.GetAxis("Horizontal"): returns a positive or negative value when the keyboard keys A or D are pressed
-transform.Translate(0, 0, speed): moves the object forward based on its z axis

##### Share on other sites
Quote:
 rotationX += Input.GetAxis("Mouse X") * sensitivityX;

That is already a problem. You don't want to accumulate rotationX over time. You want it to be the instantaneous rotation, so use "=", not "+=". You then compute the quaternion that represents how much you want to rotate in this frame, and you use it to modify an existing quaternion that represents current attitude.

The code should look more like this:
        // Controls        // Read the mouse input axis        rotationX = Input.GetAxis("Mouse X") * sensitivityX;        rotationY = Input.GetAxis("Mouse Y") * sensitivityY;        //roll the ship left and right        zRotationValue = (rotationSpeed * Input.GetAxis("Horizontal"));        Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);        Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, Vector3.left);        Quaternion zQuaternion = Quaternion.AngleAxis(zRotationValue, Vector3.forward);        wantedRotation = zQuaternion * xQuaternion * yQuaternion;                attitude *= wantedRotation;                transform.localRotation = Quaternion.Slerp(transform.localRotation , attitude, Time.deltaTime * rotationDamping);        transform.Translate(0, 0, speed);

##### Share on other sites
Welll, it didnt work either.

attitude *= wantedRotation;

transform.localRotation = Quaternion.Slerp(transform.localRotation , attitude, Time.deltaTime * rotationDamping);

It looks like the quaternion attitude never gets any value, if I change it back to wantedRotation I have the same weird rotations as before.

Also the fact that I am incrementing the rotation values over time is that the press and release key values in unity goes from 0.0f to 1.0f so if I release a key or stop moving the mouse the rotation will go back to its original 0 degrees, but thats no biggie, i can get around that.

The main concerns is still the rotations that are not right.

How is a 6DOF implementation different then a normal implementation using degrees and quaternions (basically what I am currently trying to achieve)?

##### Share on other sites
Quote:
 Original post by Zoultrextransform.localRotation = Quaternion.Slerp(transform.localRotation , attitude, Time.deltaTime * rotationDamping);

I don't know if you need damping, but it complicates things, so I would use attitude' directly.

Quote:
 It looks like the quaternion attitude never gets any value, if I change it back to wantedRotation I have the same weird rotations as before.

Do you initialize attitude to 1? You should probably also renormalize it periodically (e.g., every time).

Quote:
 Also the fact that I am incrementing the rotation values over time is that the press and release key values in unity goes from 0.0f to 1.0f so if I release a key or stop moving the mouse the rotation will go back to its original 0 degrees, but thats no biggie, i can get around that.

I am still not sure you understand why accumulating these values is bad. You are essentially using Euler angles to represent your rotation, and they are a bad way to parametrize rotations. That's the whole point of using "yawChange, pitchChange, rollChange" each frame and updating the rotation incrementally.

You can post code here using [source] and [/source] around it.

##### Share on other sites
Ha!

Alvaro you saved my life =)

It seems now to be perfect, here is the code (thanks for the []source tip, i always wondered how its done)

 [initialize function]changedRotation.w = 1.0f;[/][controls function]// Controls// Read the mouse input axisrotationX = Input.GetAxis("Mouse X") * sensitivityX;rotationY = Input.GetAxis("Mouse Y") * sensitivityY;//roll the ship left and rightzRotationValue = (rotationSpeed * Input.GetAxis("Horizontal"))*-1;Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, Vector3.left);Quaternion zQuaternion = Quaternion.AngleAxis(zRotationValue, Vector3.forward);wantedRotation = zQuaternion * xQuaternion * yQuaternion;changedRotation *= wantedRotation;        transform.localRotation = Quaternion.Slerp(transform.localRotation, changedRotation, Time.deltaTime);transform.Translate(0, 0, speed);[/]

For my surprise it seems unity doesnt have a normalize function for quaternions, if it does i cant find it, but anyway there is plenty of those around the internet, this one for example should work:
http://gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation#Normalizing_a_quaternion

Alvaro do you see anything wrong with the code now? I kept testing and its working great. Ill make it available online once Im sure its perfect.
In regard to normalizing, its too much to normalize it every frame right? Maybe every 1 second?

##### Share on other sites
Quote:
 Original post by ZoultrexAlvaro do you see anything wrong with the code now? I kept testing and its working great.

Quote:
 In regard to normalizing, its too much to normalize it every frame right? Maybe every 1 second?

I would do it every frame and only make it less frequent if it ever shows up in a profiler run (it won't).

##### Share on other sites
Quote:
 Original post by ZoultrexFor my surprise it seems unity doesnt have a normalize function for quaternions
I haven't seen this documented anywhere, but my guess is that Unity normalizes the quaternion automatically as necessary (such as after calls to Rotate() or whenever a new value is assigned to the 'rotation' or 'localRotation' field).

Although I can't cite any references that confirm this, if you assign a value of (2, 0, 0, 0) to transform.rotation and then print the results, you get (1, 0, 0 ,0), which would seem to verify that this is in fact the case.

##### Share on other sites
Quote:
 Original post by jykAlthough I can't cite any references that confirm this, if you assign a value of (2, 0, 0, 0) to transform.rotation and then print the results, you get (1, 0, 0 ,0), which would seem to verify that this is in fact the case.

Good point, that is true indeed, so Unity does normalize it by itself.
I have already seen that when I print the quaternions they are mostly 0 or when a rotation value is very different from the current, this value changes to a slightly higher value but then after a shor time it will be zero again, so I think thats whats happening, its being normalized.

I think the code for the rotation is fine, just one thing I noticed, If I apply a rotation much higher then the current rotation, I get a weird zigzag-like motion but just for like half a second and then it comes back to normal.

That wont interfere in the game really as I have now limited the mouse speed (I would have to do that anyway) and that solved the problem. Just wanted to point that out.

I will test it a bit more and will post the code here for those who in the future need a control script for a space ship.

Thank you guys for the help

##### Share on other sites
Looks like this topic never gets old. I'm also trying to implement a 6-DOF camera and I'm running into the same problems countless people seem to have run into before. Unfortunately, I have yet to find a solution that works form me. Currently, my biggest problem is unwanted z roll (this old topic has a pretty fitting description).

Basically, the control scheme that I had in mind is the following: as already mentioned, I want the camera to fly freely through the scene using 6-DOF. The position can be controlled with the "WASD" keys and the mouse wheel. If the user moves the mouse up&down/left&right while holding the left mouse button, pitch and yaw are modified. If the user moves the mouse left&right while holding the right mouse button, roll is modified.

Now, the problem is, if I hold the left mouse button and perform a cirular motion I get a LOT of unwanted roll. As long as I stick to only one axis, everything seems normal. I have pretty much worked through all the suggested remedies (such as here or here) and they either did nothing or introduced other problems. This is the version that exhibits the described unwanted roll behavior:

            if (Window.Mouse[MouseButton.Left])            {                Camera.Orientation *= Quaternion.RotateAxisAngle(new vec3(1, 0, 0), +0.01f * (float)e.YDelta) * Quaternion.RotateAxisAngle(new vec3(0, 1, 0), +0.01f * (float)e.XDelta);            }            if (Window.Mouse[MouseButton.Right])            {                Camera.Orientation *= Quaternion.RotateAxisAngle(new vec3(0, 0, 1), +0.01f * (float)e.XDelta);            }

Another thing that strikes me as odd with this version is: if I hold the left mouse down, move it left to rotate 90° around the y-axis, and then move the mouse up and down, I see roll rather than pitch. That's not supposed to happen, right?

So, another version I tried is this:

            var m = Transform.Orientate(Camera.Orientation); // matrix from quaternion            if (Window.Mouse[MouseButton.Left])            {                Camera.Orientation *= Quaternion.RotateAxisAngle(m[0].xyz, +0.01f * (float)e.YDelta) * Quaternion.RotateAxisAngle(m[1].xyz, +0.01f * (float)e.XDelta);            }            if (Window.Mouse[MouseButton.Right])            {                Camera.Orientation *= Quaternion.RotateAxisAngle(m[2].xyz, +0.01f * (float)e.XDelta);            }

In that version, pitch is always pitch, yaw is always yaw etc., but the unwanted roll still persists. At the risk of boring you, here's another one I tried:

            if (Window.Mouse[MouseButton.Left])            {                Camera.Orientation = Camera.Orientation * Quaternion.RotateAxisAngle(new vec3(1, 0, 0), +0.01f * (float)e.YDelta);                Camera.Orientation = Quaternion.RotateAxisAngle(new vec3(0, 1, 0), +0.01f * (float)e.XDelta) * Camera.Orientation;            }

Strangely enough, this one behaves pretty much exactly as the first one. I'm rather clueless what else to try at this point. As somebody mentioned in another thread: Quaternions are beautiful and easy to use as long as they work right, but not intuitive at all once you run into problems.

[Edited by - kloffy on October 21, 2010 10:57:25 PM]

##### Share on other sites
Ok, I really didn't want to post another one, but I think I have made a step in the right direction.

        private quat r = new quat(new vec3(), 1.0f);        private quat p = new quat(new vec3(), 1.0f);        private quat y = new quat(new vec3(), 1.0f);        protected void OnMove(object sender, MouseMoveEventArgs e)        {            if (Application.Mouse[MouseButton.Left])            {                p *= Quaternion.RotateAxisAngle(new vec3(1, 0, 0), +0.01f * (float)e.YDelta);                y *= Quaternion.RotateAxisAngle(new vec3(0, 1, 0), +0.01f * (float)e.XDelta);            }            if (Application.Mouse[MouseButton.Right])            {                r *= Quaternion.RotateAxisAngle(new vec3(0, 0, 1), +0.01f * (float)e.XDelta);            }            Camera.Orientation = r * p * y;        }

This has no unwanted roll *yay*, but in some orientations the axes of rotation seem switched. Kind of like in the very first version, where pitch became roll once you rotate 90° around the y axis. (Curiously, this exact case does not cause problems, but e.g. rotating 90° around x does. Maybe this has something to do with the order of multiplications?)

Anyways, the whole thing is very reminiscent of gimbal lock, which I know can happen when quaternions are used improperly. That doesn't bode well for my current code...

##### Share on other sites
Quote:
 Original post by kloffyNow, the problem is, if I hold the left mouse button and perform a cirular motion I get a LOT of unwanted roll.
I think this was pretty much covered in the threads you linked to, but this is the correct and expected behavior for full 6DOF motion. If you want different behavior than that, you'll most likely need to use a non-6DOF control scheme, or apply some sort of corrective rotation to compensate for any unwanted roll.
Quote:
 Another thing that strikes me as odd with this version is: if I hold the left mouse down, move it left to rotate 90° around the y-axis, and then move the mouse up and down, I see roll rather than pitch. That's not supposed to happen, right?So, another version I tried is this:*** Source Snippet Removed ***In that version, pitch is always pitch, yaw is always yaw etc., but the unwanted roll still persists. At the risk of boring you, here's another one I tried:
The above would seem to indicate that you're working with a math library that uses 'reverse' quaternion multiplication order rather than standard multiplication order. You can actually get the same results with your earlier code by reversing the order or multiplication; the results will be equivalent (within the limits of numerical precision, of course).
Quote:
 Ok, I really didn't want to post another one, but I think I have made a step in the right direction.*** Source Snippet Removed ***This has no unwanted roll *yay*, but in some orientations the axes of rotation seem switched. Kind of like in the very first version, where pitch became roll once you rotate 90° around the y axis. (Curiously, this exact case does not cause problems, but e.g. rotating 90° around x does. Maybe this has something to do with the order of multiplications?)
Here it looks like you've dispensed with 6DOF motion completely and reverted to simple 'from scratch' Euler angles. If so, what you're describing is to be expected; accumulated yaw and pitch will no longer result in perceived roll, but in other ways, the rotations may not behave in an expected or intuitive way.
Quote:
 Anyways, the whole thing is very reminiscent of gimbal lock, which I know can happen when quaternions are used improperly.
Hehe, it's refreshing to hear someone say that :) Usually, the comments I read online related to quaternions and gimbal lock are much less well-informed...

Anyway, as far as I can tell all the behavior you've described is both correct and expected, so it's really just going to be a matter of determining what behavior it is that you actually want. With full 6DOF motion, accumulated yaw and pitch can lead to perceived roll, so if you don't want that effect, you may not want full 6DOF motion. (That said, one option would be to incorporate some sort of 'autoleveling' feature, although that may not be appropriate in all contexts.)

##### Share on other sites
Thank you very much for taking the time to reply! I know you have answered similar questions in many threads here and it must be somewhat tiring. It's very reassuring to hear from someone who knows what he's talking about that most of the problems are expected behavior. I'll check about the order of multiplication, as this is one thing that has been bugging me: my code always looked a bit different from the suggested one.

Edit: Ah, yes indeed, one mystery is solved. When I reversed the order, I got the expected results (including unwanted roll):

            if (Window.Mouse[MouseButton.Left])            {                Camera.Orientation = (Quaternion.RotateAxisAngle(new vec3(0, 1, 0), +0.01f * (float)e.XDelta) * Quaternion.RotateAxisAngle(new vec3(1, 0, 0), +0.01f * (float)e.YDelta)) * Camera.Orientation;            }            if (Window.Mouse[MouseButton.Right])            {                Camera.Orientation = (Quaternion.RotateAxisAngle(new vec3(0, 0, 1), +0.01f * (float)e.XDelta)) * Camera.Orientation;            }`

Thanks again jyk for clearing this up!