Euler angles riddle -what kind of euler-angles are these?

Euler angles riddle -what kind of euler-angles are these?
This gave me some headache - and at
last i solved it like shown below, but first
i had tried to understand what kind of euler-angle
setting this is. I looked thru (did i miss one)
> http://cgafaq.info/wiki/Euler_angles_from_matrix
and other sources for those … sxyz, sxyx, sxzy … rxyz, rzyz …
combinations for the different usages, but none did fit.

the calculation below is the original calculation-algo that is
used in the et-source-code (gpl-ed from iD) for the setup
of the skeleton-bones (mdx-format). This is some years old
(around 10 and more years) and i suspect its the way some
old 3d-modeller-software in these days used such kind of
angle-interpretation.

Does anyone have a clue what kind it is?


def AngleVectors(angles, forward, right, up):
  angle = angles[PITCH] * (pi * 2 /360)
  sp    = sin(angle)
  cp    = cos(angle)
  angle = angles[YAW] * (pi * 2 /360)
  sy    = sin(angle)
  cy    = cos(angle)
  angle = angles[ROLL] * (pi * 2 /360)
  sr    = sin(angle)
  cr    = cos(angle)
  forward[0] = cp *cy
  forward[1] = cp *sy
  forward[2] = -sp
  right[0] = (-1*sr*sp*cy+ -1*cr* -sy)
  right[1] = (-1*sr*sp*sy + -1 *cr *cy)
  right[2] = -1 * sr * cp
  up[0] = (cr*sp*cy+ -sr * -sy)
  up[1] = (cr*sp*sy+ -sr * cy)
  up[2] = cr * cp

at the end i used this re-calculation to do the reverse lookup
(but first i wanted to try any of the mathutils matrix->euler versions
and did waste my time not finding a working one).


PITCH=0
YAW=1
ROLL=2
base_frame = 0

def degrees(x):
  return( x * 180 / pi )
def reverseAngleVectors(angles, forward, right, up):
  pitch = asin( - forward[2] )
  angles[PITCH] = degrees(pitch)
  angles[YAW] = degrees ( atan2( forward[1], forward[0] ) )
  angles[ROLL] = degrees( atan2( -right[2], up[2] ) )

Quite intresting that they do pi2/360 instead of pi180 which saves you one division.

Anyways, they convert the angle from degrees to radians.
Then they calculate the horizontal and vertical component of each of the 3 angles and calculate the resulting directional vectors. (not normalized)
Which by the way the name of the function tells you too :wink:
The end.

I am not sure what you want to do, but why would you pass degree angles to a function, transform it to radians, make a vector out of them, then reverse the vector to radians angles and transform then into degree?
Just use the degree angles and bypass the AngleVectors()

as i remember blender does not have the angles for Roll yaw and pitch

so not certain how you can convert this to Euler in Blender !

found this note here

Note: Rotation system not use by Blender
like the Heading/Pitch/Bank (or Yaw/Pitch/Roll) angles, used with the so called Tait-Bryan or cardan angles, which are a different kind of Euler rotations
http://www.shadowspawn.net/beta/quake3_mapping_yaw_pitch_roll_tut.htm

salutations

Hi test-dr,

The yaw, pitch n roll gives it away. Explained pretty well here http://planning.cs.uiuc.edu/node102.html and here http://planning.cs.uiuc.edu/node103.html The same as rotation(yaw,‘Z’) * rotation(pitch,‘Y’) * rotation(roll,‘X’) isn’t it?


from mathutils import Matrix as m
from math import radians

def rot_max(yaw, pitch, roll):
    yaw = radians(yaw)
    pitch = radians(pitch)
    roll = radians(roll)
    return m.Rotation(yaw, 3, 'Z') * m.Rotation(pitch, 3, 'Y') * m.Rotation(roll, 3, 'X')
    
print(rot_max(45,45,45))

i did not know there was a way in blender to convert form Yaw pitch and roll to some blender euler values!

when you call this function def rot_max(yaw, pitch, roll):

you give the angles in degrees for yaw pitch and roll

but then it returns a vector for each value

you dont get the simple equivalent euler angle for each value
it gives a 3 X 3 maxrix and i guess it is an euler rotation matrix

so how do you use this ?

and is there an equivalent Euler for the angle from these Yaw pitch and roll values?

thanks

It has absolutely nothing to do with Blender it’s simply a rotation around 3 axis, given Blender’s axis, Pitch’s a rotation around the Z axis, pitch around the Y axis and roll around the X axis, if you travel along the X axis. Just like batFINGER said.

If I’d like I can also call it banana, apple and tomato rotation, or Herb, Dave and Sam rotation.

that’s not the point yaw pitch and roll rotation are a special kind of euler rotation
which is not supported by blender as i know of unless it’s been changed
also indicated in reference given

and it is interesting to see there is way to do the transformation to euler angles

but i don’t see in the little script how you get the equivalent euler angles!
i can see a rotation matrix i think but then how do you use this to get the equivalent eulers angles!

thanks

Huh what are you talking about? Either it’s a linguistic barrier or you have no idea about rotation.
There’s nothing to convert. Pitch/Yaw/Roll combined are an Euler rotation, the rotation around the various axis just got named.
Euler Angles in Euclidian 3D space are per definition 3 angles. 3 axis perpendicular to each other and their angles relative to the worlds axis - Which is exactly the same as yaw, pitch and roll.

Often enough a planes yaw, pitch and roll are even used to explain Euler rotation. Those are just names for the rotations around each of the three axis.

And Tait-Bryan is simply a convention for Euler rotation for planes to start measuring the angles at the horizontal with 0 to get heading, elevation and bank.
And there’s also a different Tait-Bryan convention for airborn and land vehicles.
And for sea and space traveling there are other conventions…
But all are conventions to be used with Euler rotation.

Again, there is only one Euler Rotation, and it’s described with the 3 angles of the objects perpendicular axis relative to the “world” axis. I can call those 3 axis rotations Pitch/Yaw/Roll rotation, or Y/Z/X rotation or Icecream/Bravo/Ton rotation.
And I can use the Tait-Bryan convention to measure the angle from 0 from the horizontal and in the direction I am traveling, or in the Arexma convention, measuring the angles from a 0 pointing 45° in all directions from the world axis and ranging from 0 bananaunits to 1000 bananunits so monkeys can use my boat :wink:

What happens in the OP is that from the angles the vectors, or for the mathematical nude, the direction of the rotated axis are calculated.

And the only other representation that’s not Euler Rotation that’s commonly used are Quaternions.
And Blender supports Both.

Because there is no such thing as Euler angles. It’s Euler rotation. Angles can be anything that portions a circle into segments, we usually use Degree or Radians, or any rise/run ratio for that matter if you want to express it with 2 parameters.
The angles are the parameters for the function and they are describing the Euler rotation.

def AngleVectors(angles, forward, right, up):

Rotation(angles[pitch]/angles[roll]/angles[yaw]) == Euler(Y/X/Z)

But batFINGER already explained that very clearly.

thanks for the quick responses …

@arexma: there is no need to save a division … this is C-coding style and the compiler will reduce it to one fixed value. For my python-conversion to mdm/mdx format there is no need to save such a thing - the important thing is to keep the code-compare easy.

@batFinger: i tried those in blender – but what you did not get is the point i had a need to do the reverse lookup. Not the calculation from those euler-angles to a matrix,
i had to get the angles from an matrix back. (to say it, the mesh and bones in blender with their world-matrix had to be converted to the mdm/mdx-format, that uses angles in degrees (stuffed into a unsigned int and expanded to 65xxx for a full circle = 360 degrees).
And i did look thru things like this:
> http://www.lfd.uci.edu/~gohlke/code/transformations.py.html
and tried all options …


# map axes strings to/from tuples of inner axis, parity, repetition, frame
_AXES2TUPLE = {
    'sxyz': (0, 0, 0, 0), 'sxyx': (0, 0, 1, 0), 'sxzy': (0, 1, 0, 0),
    'sxzx': (0, 1, 1, 0), 'syzx': (1, 0, 0, 0), 'syzy': (1, 0, 1, 0),
    'syxz': (1, 1, 0, 0), 'syxy': (1, 1, 1, 0), 'szxy': (2, 0, 0, 0),
    'szxz': (2, 0, 1, 0), 'szyx': (2, 1, 0, 0), 'szyz': (2, 1, 1, 0),
    'rzyx': (0, 0, 0, 1), 'rxyx': (0, 0, 1, 1), 'ryzx': (0, 1, 0, 1),
    'rxzx': (0, 1, 1, 1), 'rxzy': (1, 0, 0, 1), 'ryzy': (1, 0, 1, 1),
    'rzxy': (1, 1, 0, 1), 'ryxy': (1, 1, 1, 1), 'ryxz': (2, 0, 0, 1),
    'rzxz': (2, 0, 1, 1), 'rxyz': (2, 1, 0, 1), 'rzyz': (2, 1, 1, 1)}

only to end up and do – what i should have done first – ignore it and
back-calculate it the brute way … (without understanding what might be the sense to use such a coding of angles).

and for a little joke … i really run into some math-errors cause some floating-point values did exceed the asin ranges for a very, very small fraction … (thats the penalty to do forward/backward calculations … a value of 1.0 will never get back as 1.0 … )

Thought the second link http://planning.cs.uiuc.edu/node103.html explained that. Note how they don’t use the simplistic asin(r31) to determine the pitch. Your experience with asin may explain why.

Also given that you have the rotation matrix how about using this to convert to Euler.


from mathutils import Matrix as m
from math import radians, degrees

def rot_max(yaw, pitch, roll):
    yaw = radians(yaw)
    pitch = radians(pitch)
    roll = radians(roll)
    return m.Rotation(yaw, 3, 'Z') * m.Rotation(pitch, 3, 'Y') * m.Rotation(roll, 3, 'X')
    
print(rot_max(45,45,45))
rm = rot_max(1, 1, 1)
er = rm.to_euler('XYZ')  # use whatever euler type
er_d = [degrees(a) for a in er]  #euler rotations in degrees
print(er_d)

import bpy

#spin the cube
mw = bpy.context.object.matrix_world.to_3x3() 

bpy.context.object.matrix_world = (mw * rm).to_4x4()  

@batFinger: thanks again, i did try this in blender and not only for XYZ.
From what you have the opinion the representation of angles used in this game-model is euler-XYZ?
If you dont know what kind - how to find it out? Can you “see” it from my working back-calculations what it is?

I even tried such a brute force to check what it might be:


#!/usr/local/bin/python3.2
#
import math


# axis sequences for Euler angles
_NEXT_AXIS = [1, 2, 0, 1]

# map axes strings to/from tuples of inner axis, parity, repetition, frame
_AXES2TUPLE = {
    'sxyz': (0, 0, 0, 0), 'sxyx': (0, 0, 1, 0), 'sxzy': (0, 1, 0, 0),
    'sxzx': (0, 1, 1, 0), 'syzx': (1, 0, 0, 0), 'syzy': (1, 0, 1, 0),
    'syxz': (1, 1, 0, 0), 'syxy': (1, 1, 1, 0), 'szxy': (2, 0, 0, 0),
    'szxz': (2, 0, 1, 0), 'szyx': (2, 1, 0, 0), 'szyz': (2, 1, 1, 0),
    'rzyx': (0, 0, 0, 1), 'rxyx': (0, 0, 1, 1), 'ryzx': (0, 1, 0, 1),
    'rxzx': (0, 1, 1, 1), 'rxzy': (1, 0, 0, 1), 'ryzy': (1, 0, 1, 1),
    'rzxy': (1, 1, 0, 1), 'ryxy': (1, 1, 1, 1), 'ryxz': (2, 0, 0, 1),
    'rzxz': (2, 0, 1, 1), 'rxyz': (2, 1, 0, 1), 'rzyz': (2, 1, 1, 1)}

_TUPLE2AXES = dict((v, k) for k, v in _AXES2TUPLE.items())

_EPS = 0.000000001

def euler_from_matrix(matrix, axes='sxyz'):
    """Return Euler angles from rotation matrix for specified axis sequence.

    axes : One of 24 axis sequences as string or encoded tuple

    Note that many Euler angle triplets can describe one matrix.

    >>> R0 = euler_matrix(1, 2, 3, 'syxz')
    >>> al, be, ga = euler_from_matrix(R0, 'syxz')
    >>> R1 = euler_matrix(al, be, ga, 'syxz')
    >>> numpy.allclose(R0, R1)
    True
    >>> angles = (4*math.pi) * (numpy.random.random(3) - 0.5)
    >>> for axes in _AXES2TUPLE.keys():
    ...    R0 = euler_matrix(axes=axes, *angles)
    ...    R1 = euler_matrix(axes=axes, *euler_from_matrix(R0, axes))
    ...    if not numpy.allclose(R0, R1): print(axes, "failed")

    """
    try:
        firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()]
    except (AttributeError, KeyError):
        _TUPLE2AXES[axes]  # validation
        firstaxis, parity, repetition, frame = axes

    i = firstaxis
    j = _NEXT_AXIS[i+parity]
    k = _NEXT_AXIS[i-parity+1]

    M = matrix #[[0,0,0],[0,0,0],[0,0,0]] #numpy.array(matrix, dtype=numpy.float64, copy=False)[:3, :3]
    if repetition:
        sy = math.sqrt(M[i][j]*M[i][j] + M[i][k]*M[i][k])
#        print("sy:", sy)
        if sy > _EPS:
            ax = math.atan2( M[i][j],  M[i][k])
            ay = math.atan2( sy,       M[i][i])
            az = math.atan2( M[j][i], -M[k][i])
        else:
            ax = math.atan2(-M[j][k],  M[j][j])
            ay = math.atan2( sy,       M[i][i])
            az = 0.0
    else:
        cy = math.sqrt(M[i][i]*M[i][i] + M[j][i]*M[j][i])
#        print("cy:", cy)
        if cy > _EPS:
            ax = math.atan2( M[k][j],  M[k][k])
            ay = math.atan2(-M[k][i],  cy)
            az = math.atan2( M[j][i],  M[i][i])
        else:
            ax = math.atan2(-M[j][k],  M[j][j])
            ay = math.atan2(-M[k][i],  cy)
            az = 0.0

    if parity:
        ax, ay, az = -ax, -ay, -az
    if frame:
        ax, az = az, ax
    return ax, ay, az

mat = [[ 0.8780784606933594, -0.20382282137870789, -0.43293705582618713],
        [0.13696907460689545, 0.9739492535591125, -0.18072718381881714],
                [0.45849505066871643, 0.09939365088939667, 0.8831212520599365]
    ]                        

print(mat)
euler = euler_from_matrix(mat, "sxyz")
print(euler)
for angles in [ [0,0,0], [0,90,0], [90,0,0], [90,0,90], [45,45,45], [30,10,-120], [90,180,45] ]:
#  angles = [0,90,0]
  mat = [ [0,0,0], [0,0,0], [0,0,0] ]
  AngleVectors(angles, mat[0], mat[1], mat[2])
  print("test:", angles)
  reverseAngleVectors(angles, mat[0], mat[1], mat[2])
  print("rev.:", angles)
if False:
#  for i in range(3):
#    mat[1][i] *= -1
#  print(mat)
  mat2 = [ [ mat[0][0], mat[1][0], mat[2][0] ], [ mat[0][1], mat[1][1], mat[2][1] ], [ mat[0][2], mat[1][2], mat[2][2] ] ]
  for axstr in _AXES2TUPLE.keys(): 
    #print(axstr)
    angles = [ degrees(f) for f in euler_from_matrix( mat, axes=axstr) ]
    print(axstr, angles)

i have to give credit to the one for this python-matrix-to-euler routine … but i dont find the link the quick way …
i think this: http://www.lfd.uci.edu/~gohlke
(hell and again some char-conversion … might be a “tilde”)

edit found link: http://www.lfd.uci.edu/~gohlke/code/transformations.py.html

ok … just why this all …
i still have a tripod in my head and its totally wrong …

its all so easy – the default for md3-objects is with a rotation around z-axis 90-degrees – and this (the picture) is a combination of this and the mdm/mdx-export and the blender-orientation …

believe me or not – i already had the head upside-down …
blender uses the y-axis as the main-view-direction and (i dont know who desided this and why) the default armature setting (like rigify-biped) has its facing -180 degrees rotated z-axis ( this is not in the y-axis direction, its “backwards” and if there is a mistake … one will end up with left and right switched).

… and so on …

and if i say “its all so easy” … this is a bad joke …;-( (for my little brain … )

Attachments


The AngleVectors routine you posted creates a rotation matrix exactly the same as described in the links i posted. So using the definitions there you can get the yaw, pitch and roll straight from your matrix in euler XYZ they will be equivalent to (roll, pitch ,yaw)
Looking closer to your code you have a different order in the angle vector. This implies to me that it is y up and z forward (look at notes in code) . You can use the axisconvert routines in bpy_extras to change this around.

I put in the matrix you had in a previous post.


from mathutils import Matrix as m
from mathutils import Vector
from math import radians, degrees
from bpy_extras.io_utils import axis_conversion

def rot_max(yaw, pitch, roll):
    yaw = radians(yaw)
    pitch = radians(pitch)
    roll = radians(roll)
    return m.Rotation(yaw, 3, 'Z') * m.Rotation(pitch, 3, 'Y') * m.Rotation(roll, 3, 'X')
    


# test with your matrix

mat = [[ 0.8780784606933594, -0.20382282137870789, -0.43293705582618713],
        [0.13696907460689545, 0.9739492535591125, -0.18072718381881714],
                [0.45849505066871643, 0.09939365088939667, 0.8831212520599365]
    ]             
er = m(mat).to_euler('XYZ')

er_d = [degrees(a) for a in er]  #euler rotations in degrees



# this will come out as rotation_euler 'XYZ' = roll,pitch,yaw

roll, pitch, yaw = er_d
'''
(Heading, Elevation and Bank = yaw pitch roll)

In your code you have
PITCH=0
YAW=1
ROLL=2

whereas using the definition for roll pitch yaw in the link i get
ROLL=0  it was rolled around the x axis
PITCH=1        pitched           y
YAW=2          yawed             z



it would appear then that your input data has 

y up
z forward ?



'''
print("euler xyz rotation from rotation matrix  (roll, pitch, yaw)")
print(er_d)
print("rotation matrix from yaw, pitch roll calculated from matrix")
print(rot_max(yaw,pitch,roll))

diff = rot_max(yaw,pitch,roll) - m(mat)
print("input matrix minus calculated rotation matrix")
print(diff)

print("converted from z up x forward to y up z forward")
convert = axis_conversion('Z','X','Y','Z')
#print(convert)

print(convert * Vector(er_d))