2
\$\begingroup\$

I am trying to convert the orientation of an OpenVR controller that I have stored as a glm::vec3 of Euler angles into a glm::fquat and back, but I get wildly different results and the in-game behavior is just wrong (hard to explain, but the orientation of the object behaves normally for a small range of angles, then flips in weird axes).

This is my conversion code:

// get `orientation` from OpenVR controller sensor data

const glm::vec3 eulerAnglesInDegrees{orientation[PITCH], orientation[YAW], orientation[ROLL]};
debugPrint(eulerAnglesInDegrees);

const glm::fquat quaternion{glm::radians(eulerAnglesInDegrees)};
const glm::vec3 result{glm::degrees(glm::eulerAngles(quaternion))};
debugPrint(result);

// `result` should represent the same orientation as `eulerAnglesInDegrees`

I would expect eulerAnglesInDegrees and result to either be the same or equivalent representations of the same orientation, but that is apparently not the case. These are some example values I get printed out:

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.7636 144.849 44.3845 
-147.236 35.1512 -135.616 

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.0103 137.415 45.1592 
-147.99 42.5846 -134.841 

As you can see above, for some orientation ranges the conversion is correct, but for others it is completely different.

What am I doing wrong?

I've looked at existing questions and attempted a few things, including trying out every possible rotation order listed here, conjugating the quaternion, and other random things like flipping pitch/yaw/roll. Nothing gave me the expected result.

How can I convert euler angles to quaternions and back, representing the original orientation, using glm?


Some more examples of discrepancies:

original:      4; 175;   26; 
computed:   -175;   4; -153; 
difference:  179; 171;  179; 

original:     -6; 173;   32; 
computed:    173;   6; -147; 
difference: -179; 167;  179; 

original:      9; 268;  -46; 
computed:   -170; -88;  133; 
difference:  179; 356; -179; 

original:    -27; -73;  266; 
computed:    -27; -73;  -93; 
difference:    0;   0;  359; 

original:    -33; 111;  205; 
computed:    146;  68;   25; 
difference: -179;  43;  180; 

I tried to find a pattern to fix the final computed results, but it doesn't seem like there's one easy to identify.


GIF + video of the behavior:

Video excerpt


Visual representation of my intuition/current understanding:

Visual diagram

  • The above picture shows a sphere, and I'm in the center. When I aim the gun towards the green half of the sphere, the orientation is correct. When I aim the gun towards the red half of the sphere, it is incorrect - it seems like every axis is inverted, but I am not 100% sure that is the case.
\$\endgroup\$
1
  • \$\begingroup\$ What operation are you performing after the round-trip conversion that's revealing unwanted behavior? There's a decent chance the error is there, and the change of angle representation is just making it visible. \$\endgroup\$
    – DMGregory
    Commented Feb 22, 2020 at 3:08

1 Answer 1

4
\$\begingroup\$

The results are not what you expect, but they are not wrong. It’s just that for a given orientation there are at least two “paths” through Euler angles that lead there.

For instance, the identity quaternion is trivially converted to Euler angles [0,0,0]. But doing three 180-degree rotations around each axis leaves you in the same orientation, too. That means that [180,180,180] is a valid Euler angle representation of that quaternion, too.

Now since your character appears to be a human, it’s probable that their head can’t roll at 180-degree angles, so here is a simple method that you can use to fix your values:

if (std::fabs(result.z) >= 90) {
    result.x += 180.f;
    result.y = 180.f - result.y;
    result.z += 180.f;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .