Search Unity

bug in unity API quaternion.euler on x axis only

Discussion in 'Scripting' started by gaiastellar, Jun 4, 2019.

  1. gaiastellar

    gaiastellar

    Joined:
    Nov 8, 2013
    Posts:
    57
    hi, this, i believe is a bug in the API, either in Transform.eulerAngles.x or Quaternion.Euler, but im not sure how to report a bug in the unity api.

    i created a ui to display the euler angles of the x,y and z axis' of an object being rotated. i rotated the object all the way round one axis at a time. the y and z axis , as expected, display euler angles 0-360 degrees, and then beck to zero. the x axis however, go like this - 0-90, then 90-0, then 270-360, then 360-270. it completely missed the range 90-270 degrees. i discovered this because i was trying to create a minimap that copied the x axis rotation of the camera, but it just went back scross only half a rotation. i tried copying the objects y and z axis instead and they worked fine, so in the end i created a ui to display the euler angles to see if i could find out what was going wrong, and the x axis euler angles are not being translated properly from quaternions in the API. they display fine in the transform of course, but this i suspect uses a different section of code. in this post https://answers.unity.com/questions...ssues.html?childToView=1637738#answer-1637738

    someone came up with a method to convert between quaternions and euler angles which works as expected. the code is here:

    Code (CSharp):
    1. //This function converts a quaternion to a euler angle without triggering gimbal lock
    2. public static Vector3 ConvertQuant2Euler(Quaternion quaternion) {
    3.      float tempEuler;
    4.      float[] eulerAngles = new float[3];
    5.      //Convert pitch - X
    6.      tempEuler = Mathf.Atan2(2 * quaternion.x * quaternion.w + 2 * quaternion.y * quaternion.z, 1 - 2 * quaternion.x * quaternion.x - 2 * quaternion.z * quaternion.z);
    7.      eulerAngles[0] = tempEuler * 180 / Mathf.PI;
    8.      //Convert yaw - Y
    9.      tempEuler = Mathf.Asin(2 * quaternion.x * quaternion.y + 2 * quaternion.z * quaternion.w);
    10.      eulerAngles[1] = tempEuler * 180 / Mathf.PI;
    11.      //Convert roll - Z
    12.      tempEuler = Mathf.Atan2(2 * quaternion.y * quaternion.w + 2 * quaternion.x * quaternion.z, 1 - 2 * quaternion.y * quaternion.y - 2 * quaternion.z * quaternion.z);
    13.      eulerAngles[2] = tempEuler * 180 / Mathf.PI;
    14.      return new Vector3(eulerAngles[0], eulerAngles[1], eulerAngles[2]);
    15. }
     
    Last edited: Jun 4, 2019
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Converting from Euler angles to a Quaternion is deterministic and predictable, but going the other way isn't. Never has been, never will be. Here's a longer writeup.
    If your Euler's X value is an unexpected number, as you mention here, then the Euler's Y and Z values will be different numbers that will make that X value work correctly. Your posted algorithm produces one valid Euler interpretation of the Quaternion, and Unity's algorithm produces a different one, but they're both correct and valid.

    Unless you've converted the returned Euler angles back into a Quaternion and gotten a different Quaternion value, it's not a bug.
     
    DonLoquacious likes this.
  3. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Yep, not a bug. If you're having a specific problem that you're trying to solve by digging into quaternions, it's better to just state the problem. If you're accessing the x,y,z,w values of a quaternion directly to do things with them, you've already stepped off of a cliff.
     
    Joe-Censored likes this.
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,527
    I'll include the documentation for 'eulerAngles' from Unity:
    https://docs.unity3d.com/ScriptReference/Quaternion-eulerAngles.html

    It specifically unravels the quat in the order z, x, y.

    Z takes precedence as a result.

    All your algorithm does is change that order. So as a result you may get x precedence the way you like, but if I were to use it I'd lose my z precedence if I wanted that instead.

    Though mind you I haven't fully tested the algorithm you've sourced to see what precedence order it actually gives. Though I'm highly suspect of it for the fact that the author misspelled Quat as Quant. And they instantiate an array of length 3 instead of easily using 3 variables... which says to me that they either 1) don't understand C#/mono/unity very well and/or 2) they did a blind conversion of a C++ implementation of the algorithm. Both of which concern me since well this means you've copied an algorithm from an author who copied the algorithm from god knows where... 3rd generation fish story going on here!

    Also I love this comment:
    //This function converts a quaternion to a euler angle without triggering gimbal lock

    Gimbal lock is not a result of conversion. It's the result of a change/delta of rotation around a gimbal that is locked. Conversion is not a change/delta in rotation, it's just a conversion...

    It's like saying "this function converts kg to lbs without triggering the difference in weight based on the gravity of the nearest massive object"... what?

    As I tell the team I work with here at my job... if you're copying code from stackoverflow or some other unknown internet source, and you don't know what it ACTUALLY does... you're doing it wrong.
     
    Last edited: Jun 4, 2019
  5. gaiastellar

    gaiastellar

    Joined:
    Nov 8, 2013
    Posts:
    57
    hi , thanks for all your replies. just to clarify, i wasnt trying to use quaternions directly - i was just trying to get one objects z axis to match another objects x axis using euler angles. it was working absolutely fine copying the y and z axis, but not the x. also, the object i was copying was only rotating on its x axis. the local rotation was zero on the y and z axis. I get what you're all saying regarding the code i got from the internet to solve this problem. i had searched extensively for how to solve this problem, and while i found several people with the same issue, there had only been one solution suggested, which is the code i included in my original message. and despite the various critcisms of it, the fact is it works, on any axis, no matter what, and it produces a sensible 0-360 degree value for the rotation of all 3 axis', which unity cant seem to do. as far as i can see, the proof is in the pudding, and the pudding tastes good!!
    thanks
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,527
    I had no idea 0-360 was the 'sensible' rotational values.

    I always preferred -180 - 180 personally... but alas, that's me. I guess I'm not sensible.

    Which is kind of the whole problem here... what is "sensible" is really just opinion. 10 = 370 = 730 = -350 = -710

    It's rotations... for any given 2d orientation there's more than one value that works for the same rotation. In 3d, there's 3 axis of rotation, all 3 of which have multiple sensible values per axis.

    90 degrees around x is the same orientation as -270 around x, 270 around y, and -90 around z. They're identical.

    And since Quats aren't in euler, it doesn't know what euler you meant or intended. It also doesn't know what is "sensible" either because "sensible" is a human term. It's like asking math what a beautiful sunset is... it can only tell you the trajectory of the earth around the sun causing an angle of approach relative to your position on the surface of the earth so that the sun appears that the sun is setting over the horizon. It'll leave the beauty of it up to you.

    ...

    [Edit]

    Oh and I'll mention there's a rhyme to the reason Unity picked the order z, x, y when unraveling a Quat into an Euler.

    It means that y will have a respective angle always (you want x to).

    Why would they want y to be respected? Well... because y is up in Unity. It means you can easily read the y axis for facing information without having to do any odd math to it (like you have to for x). It's the same reason why Quaternion.LookAt defaults to an up axis of Vector3.up, and so much more of the engine.

    This is called a "left-handed y-up coordinate system". And it just happens to be what unity uses. They had to pick one.

    You just so happened to fall into that scenario where y is not significant to you. But instead x is.

    In your situation, if I wanted to get some angle around an axis... well I wouldn't want to. Because in 3-space it doesn't really make sense to. Instead I usually think of it in terms of "I want the angle between 2 vectors around some axis in 3-space".

    For example if I wanted the angle around the x-axis... well what does that mean? Angle off of what? So I might say "I want the angle of my forward vector off <0,0,1> around <1,0,0> (x-axis)". And I'd use something like Vector3.SignedAngle to get that:
    https://docs.unity3d.com/ScriptReference/Vector3.SignedAngle.html

    Of course it measure -180 to 180, which makes sense since you know... sine and pi and what not. But if you wanted 0->360 you'd just add 360 if it's < 0.
     
    Last edited: Jun 5, 2019