I am given a rotation matrix constant
that is immutable and should not be modified with rotations of 30
, 20
, and 10
degrees about XYZ.
I need to create a matrix attempt
that, when either pre or post multiplied with constant
will create a matrix with rotations of 90
, -20
, and -10
degrees about XYZ and equal to target
.
Matrix4d target = new Matrix4d()
.rotationXYZ(Math.toRadians(30 + 60), Math.toRadians(20 - 40), Math.toRadians(10 - 20)); // 90, -20, -10
System.out.println(new Vector3d(8.0).mulProject(target)); // Target * V = (5.973 -10.687 6.489)
Matrix4d constant = new Matrix4d()
.rotationXYZ(Math.toRadians(30), Math.toRadians(20), Math.toRadians(10)); // A
Matrix4d attempt = new Matrix4d()
.rotationXYZ(Math.toRadians(60), Math.toRadians(-40), Math.toRadians(-20)); // B
Matrix4d combined = constant.mulLocal(attempt, new Matrix4d()); // C = B * A
System.out.println(new Vector3d(8.0).mulProject(combined)); // Combined * V = (1.84 -10.747 8.55)
As shown above, the attempt
matrix rotates by the extra amount required in each axis after the constant
. This resulting combined
matrix does not equal the target
though.
Matrix4d attempt = new Matrix4d()
.rotationXYZ(Math.toRadians(90), Math.toRadians(-20), Math.toRadians(-10))
.mul(constant.invert(new Matrix4d()));
This second attempt works, but it requires that I inverse the work of constant
first which is a waste of resources. I would like to find the correct rotation values to give to rotationXYZ
or rotate
with a quaternion without inversing the previous matrix.
First we must know precisely what rotateXYZ does. From https://joml-ci.github.io/JOML/apidocs/org/joml/Matrix4f.html#rotateXYZ(float,float,float,org.joml.Matrix4f)
Apply rotation of angleX radians about the X axis, followed by a rotation of angleY radians about the Y axis and followed by a rotation of angleZ radians about the Z axis.
So we can think of this as apply rotations Rx, Ry, Rz to our starting matrix M to give resulting matrix M'.
M' = Rz Ry Rx M
Now a very important point about matrix multiplication is that order of applying matrices is important, i.e. it is not commutative. So using a different order say
M'' = Rx Ry Rz M
will in general be different to M'.
Here we have two matrices
target = Rz(-10) * Ry(-20) * Rx(90)
constant = Rz(10) * Ry(20) * Rx(10)
and wish to find matrix attempt
such that
target = attempt * constant
Or
target = attempt * Rz(10) * Ry(20) * Rx(10)
Now we can apply the inverse of the compentent rotations. Note that the inverse of a rotation matrix Rx(theta) is the rotation matrix Rx(-theta), (a clockwise rotation is the inverse of an anti-clockwise rotation).
So we apply the rotations applying the inverses on the right
target = attempt * Rz(10) * Ry(20) * Rx(10)
target * Rx(-10) = attempt * Rz(10) * Ry(20)
target * Rx(-10) * Ry(-20) = attempt * Rz(10)
target * Rx(-10) * Ry(-20) * Rz(-10) = attempt
Note the difference in order the last matrices are applied in the order Z, Y, X.
Now JOML has method for matrices in every possible order and the method we want is
public Matrix4f rotateZYX(Vector3f angles)
Apply rotation of angles.z radians about the Z axis, followed by a rotation of angles.y radians about the Y axis and followed by a rotation of angles.x radians about the X axis.
So to find attempt we can take
inverse = new Matrix4d()
.rotationZYX(Math.toRadians(-10), Math.toRadians(-20), Math.toRadians(-10))
attempt = target.clone()
attempt.mul(inverse) // i.e. attempt = target * inverse
Or more simply
attempt = target.clone()
attempt.rotationZYX(Math.toRadians(-10), Math.toRadians(-20), Math.toRadians(-10))