4x4 Matrix to Transform Values?


#1

Hey guys, so I have a 4x4 matrix of values that I would like to convert into transform values. Basically I want to do the exact same thing as the decomposeMatrix node does, except I am interested in the actual math behind it.

Keep in mind, my eyes go crosseyed when I try to find formula’s on Wikipedia, as math isn’t my best friend. I do understand programmer notation - if that makes any sense?

If someone could shed some light on how the decomposeMatrix gets its translate, rotate and scale values from a 4x4 matrix, that would be rad.

Thanks!


#2

No one has any ideas?


#3

What do you mean by transform values? Translation, Rotation, Scale? What is rotation for you? Quaternion, RotationAxis, Euler?


#4

Take a look here


#5

@ denisT - Yep, translate, rotate and scale values. Should have specified, that I am trying to get Euler XYZ rotational values. I’ve already figured out the translate and scale, but the rotational Euler values are giving me trouble. The problem I’ve been having is a lot of formulas assume a different rotation order instead of XYZ.

@ Buexe - I’ve taken a look at that page, but I’m not sure how to actually convert from the matrices to Euler values. Unless it is there and I cannot understand it.

Something on that page that would be helpful would be a breakdown for getting different Euler values like:

(Pseudo Code)
Euler (XYZ)
rotationX = (magic code here)
rotationY = (magic code here)
rotationZ = (magic code here)

Euler (ZXY)
rotationX = (magic code here)
rotationY = (magic code here)
rotationZ = (magic code here)

etc …


#6

MTransformationMatrix can be constructed by initializing it with a normal MMatrix, and it has a rotation() method that returns a MEulerRotation (or MQuaternion if you wish).
Cast your matrix to MTransformationMatrix (if you haven’t already), then use that to extract.
Once you have an MEulerRotation you can set its order and retrieve the triplet of floats for the three angles.
It has similar translation() and scale() methods so you can fetch everything from the one place.

Look up MTransformationMatrix in the documentation for the details. It’s available in both C++ and both OpenMaya wrappers.

What you want boils down to:
tMtx = MTransformationMatrix(myMtx)
mRot = tMtx.rotation()
mRot.reorderIt(newRotOrderConstant)
tripletOfAngles = mRot.asVector() //maya trivector, you can then get x, y and z from it


#7

@ ThE_JacO - I’m not using the API, I am more interested in the actual math behind it.


#8

The math behind it you could have just googled for to be honest.
This is one of the most approachable and complete papers on the subject:
http://www.staff.city.ac.uk/~sbbh653/publications/euler.pdf

It largely boils down to trig derivative angles from vectors being offset and projected on cardinal axes in order (rot order matters), and, since it’s not a unequivocal operation end to end, managing the branching possibilities of the polynomial to produce deterministic results.
In less pompous sounding words: It takes the axes in the matrix as vectors, puts their terms (x, y, z) on the unit circle (trigonometry component), and figures out bit by bit, in reverse, how much of an angle is needed to produce that axis. For some combinations those operations would be invalid, so one check to prevent a divide by zero is required mid-way to choose how to do the next steps.

Doing it in a computationally optimal way is a different matter, it’s a tricky operation to pack in a truly efficient cycle count and it will usually require clever use of intrinsics to pack, mask and unpack quadruplets in the most effective manner.


#9

let’s try to practice in python and math:

import math
import maya.cmds as cmds

make a test:

cmds.file(f=1, new=1)
b = cmds.polyCube()
cmds.rotate(20, 30, 40, b)
 print (cmds.xform(b, q=1, rotation = 1))

now math -> matrix4x4 to euler angles in order XYZ:

def matrix4x4_to_euler_xyz(mat):
	x = math.atan2(mat[1][2], mat[2][2])
	y = -math.asin(mat[0][2])
	z = math.atan2(mat[0][1], mat[0][0])
	return [math.degrees(x), math.degrees(y), math.degrees(z)]

so we have:

mat16 = cmds.xform(b, q=1, matrix = 1) # matrix 16 values
mat = [a[i:i+4] for i in range(0, len(a), 4)] # matrix 4x4
print matrix4x4_to_euler_xyz(mat)

hopefully i’ve not missed anything


#10

@ denisT - THANK YOU!! I love your brain so much! This was exactly what I was looking for. Cheers!
:buttrock: :bounce: :applause: :keenly:


#11

my brain is a closet with lost things… usually i keep it closed.
i would solve your task for myself as ThE_JacO showed above


#12

Solid piece of help, but a couple notes worth making IMO:
Functionally speaking that will drift on cos0 cases.
E.G: cmds.rotate(20, 90, 0, b) will (I think) drift off on the Z, and 0,90,20 will drift on X.
Past a certain point or sign drift will transfer (it can’t be accounted for post facto, it needs to be factored in before atan2s).

Missing the cosine term and not checking for the cos0 case (both things outlined in the paper I linked) will have many invalid fringes, when that happens the terms to look up in the matrix change, so this can’t be solved with a single triplet that goes end to end for each row.

And for the sake of understanding I find the extended explanation (again in paper linked) tends to be easier to visualize. Sin and cos terms of vector components have immediate representation on the unit circle of the angular offsets being produced as you apply the chain of ops to the decomposed matrix and are important to understanding what is going on.


#13

see my post above… :slight_smile:

sure we have to do a gimbal lock check. or… use a built-in


#14

We appear to have posted at the same time :slight_smile:
For the record, I wasn’t dissing the post, it’s great to have a concise snippet of code in there.
I also edited my original one because it was siggraph-paper levels of unintelligible words.


#15

EuclideanSpace.com has a nice explanation as well, WITH PICTURES!