- Home /
quaternion.eulerAngles 1:1 angle conversion
Hi,
I've fallen onto this wierd behaviour when assigning rotation to a Quaternion via the eulerAngles.
The following code:
targetRotation.eulerAngles = targetAngle;
targetRotationAngles = targetRotation.eulerAngles;
where targetAngle & targetRotationAngles are Vector3() and targetRotation is Quaternion(), works fine but only when targetAngle.x is in range 0-90. For targetAngle = (91, 0, 0) you get targetRotationAngles = (89, 180, 180) where I would expect (91, 0, 0).
How to achieve 1:1 mapping if I want to store angles in the Quaternion rather than a Vector3 ???
This gives me a really hard time. Please help. Many thanks.
Code ready to reproduce the problem:
using UnityEngine;
public class QuaternionTest : MonoBehaviour {
public Vector3 targetAngle;
public Vector3 targetRotationAngles;
private Quaternion targetRotation;
// Update is called once per frame
void Update () {
targetRotation.eulerAngles = targetAngle;
targetRotationAngles = targetRotation.eulerAngles;
}
}
Answer by troien · Dec 07, 2017 at 07:45 PM
Simply put, you can't. EulerAngles are not Quaternions. You can convert between the 2. But a lot of values (if not all) can be represented in more then one way in eulerangles. For instance, set the Eulerangles of any object in the scene (using the inspector) to (90,-90,180). another one to (90,90,0). And yet another one to (90, 45, -45). Add this script to each object you set these eulerangles to.
public class Example : MonoBehaviour
{
private void Awake()
{
Debug.Log(transform.rotation.eulerAngles);
}
}
Hit play and you'll notice this is the exact same rotation... So the Quaternion has no knowledge on which one it should choose when converting this rotation to eulerangles. It picks the one that makes most sense (Don't know the exact reason of why it picks certain options over the other)
You could compare it with converting hsv to rgb. If 'value' is 0, rgb is going to be black, no matter what you set the hue or saturation (you can check this in any colorpicker with hsv in it), the resulting rgb is always black. So if you convert rgb black to hsv. Which hue or saturation should it pick if it doesn't know the original input? In this example, it would pick 0 for both hue and saturation because that is the most logical one to pick in most cases when you didn't convert it from hsv to rgb first.
A quaternion doesn't know the original input in euler angles. And even if it did know the last input, not only would this hit performance, it would also encounter the same problem if you would rotate a quaternion by anything else then eulerangles, what eulerangles representation should it pick then? You might be able to calculate this if you have store the original eulerangles, but I don't have enough knowledge about quaternions to help you with that...
If you want to work with eulerangles, work with eulerangles, convert to quaternion if you need to set an objects rotation. But don't convert it back. Keep your own eulerangles and use those for your calculations if you need to use eulerangles for your calculations.
Thanks mate.
I'm not an expert on quaternions and rotations but to me this is not intuitive when you have the object's rotation pictured as localEulerAngles but inside the system rotations are stored in such a way that no one would ever want to use it ...
Sadly this doesn't make life easier when it comes to rotating objects around World.Self by a predefined angle. Not to mention a controlled way o executing RotateTowards() method. Until now I thought that Quaternions are a most logical place to handle angle rotations but obviously they are not.
Well, Quaternions are in Unity the most logical place to store and handle rotations of any kind. As quaternions don't suffer from gimbal lock and you can still apply rotations via angles with it. The trouble comes when you want to keep using the eulerangles for display to user for example, It looks like Unity internally actually manages to do something like this in the inspector of a transform, Below is how you can call that code. Don't know if it works in a finished build and I can't check what the code actually does, as the code I'm calling gets that value from unmanaged code... Wouln'td advise using it in code you intend to publish. In fact, wouldn't advise using it at all :p Also, this value is retrieved from a Transform, not directly from a Quaternion.
$$anonymous$$ethodInfo info = transform.GetType().Get$$anonymous$$ethod("GetLocalEulerAngles", BindingFlags.Instance | BindingFlags.NonPublic);
$$anonymous$$ethodInfo[] test = transform.GetType().Get$$anonymous$$ethods(BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Log(info.Invoke(transform, new object[] { 4 })); // 4 here stands for zxy (RotationOrder)
But looking at what you said. You should look at the documentation. Especialy this line might be useful.
You can use the Quaternion.operator * to rotate one rotation by another, or to rotate a vector by a rotation.
So rotating a Quaternion by an angle, not bothering about what the result in euler angles is could be done like this:
public class Example : $$anonymous$$onoBehaviour
{
private void Update()
{
//transform.rotation = Quaternion.Euler(0, 90 * Time.deltaTime,0) * transform.rotation; // Rotate around world y
transform.rotation = transform.rotation * Quaternion.Euler(0, 90 * Time.deltaTime, 0); // Rotate around local y
}
}
Now that I mention it, Quoternion.AngleAxis is probably better ins$$anonymous$$d of Quaternion.Euler. Because when doing local rotations, in most cases it makes sense to rotate one axis at a time ins$$anonymous$$d of 2. AngleAxis enforces this. For instance rotation by [90,90,0] or by [90,0,0] and then [0,90,0] will yield different results because the y axis is different when the second rotation is applied (i.e. it now points forward ins$$anonymous$$d of up because we just rotated x by 90 degrees). Depends on your use case though I suppose.