Assuming the following matrix multiplication convention, where \$M\$ is the matrix you want to decompose, and the \$\vec p\$ variables represent points as column vectors with a w coordinate \$p_w = 1\$...
$$\begin{array}{lc}
\vec p_{transformed} = M \times \vec p_{local}
& M=\begin{bmatrix}
m_{00} & m_{01} & m_{02} & m_{03} \\
m_{10} & m_{11} & m_{12} & m_{13} \\
m_{20} & m_{21} & m_{22} & m_{23} \\
0 & 0 & 0 & 1 \\
\end{bmatrix} \\
& \text{where $M$ is assumed to be}\\
& \text{decomposible into: }\\
\vec p_{transformed} = T \times R \times S \times \vec p_{local} &M = T \times R \times S\\
\end{array}$$
Here the component matrices are:
Translation by a vector \$\vec t = (t_x, t_y, t_z)\$:
$$T=\begin{bmatrix}
1 & 0 & 0 & t_x \\
0 & 1 & 0 & t_y \\
0 & 0 & 1 & t_z \\
0 & 0 & 0 & 1 \\
\end{bmatrix}$$
Rotation by an orthonormal rotation matrix \$R\$ (with determinant 1), and...
Axis-aligned scale by \$(s_x, s_y, s_z)\$:
$$S=\begin{bmatrix}
s_x & 0 & 0 & 0 \\
0 & s_y & 0 & 0 \\
0 & 0 & s_z & 0 \\
0 & 0 & 0 & 1 \\
\end{bmatrix}$$
Then it is possible to compute a choice of translation \$T\$, rotation \$R\$, and scale \$S\$ with the same combined result as \$M\$.
But there is no guarantee that these will be the same values originally used to create \$M\$, as we'll see below.
You also may lose precision due to rounding errors along the way, so you should avoid repeated round-trip conversions between \$M\$ and \$T \, R \, S\$, such as when laying out objects in a level editor, saving and loading as you go.
This also goes for updating the transform of a dynamic object at runtime. Being able to rebuild the matrix from raw translation, rotation, and scale values will help you avoid harmful rounding errors.
So, it will almost certainly be worth keeping the extra memory to store your original transformation parameters, both for consistency from a user's perspective, and for accuracy/stability.
That said, if it's something you're using in a read-only manner, or you need to reconstruct transformation parameters from baked data which only contains combined matrices, you can do it like this...
First, the translation vector is the image of the origin position vector after transformation:
$$\vec t = M \times \begin{bmatrix}0\\ 0\\ 0\\ 1\end{bmatrix}$$
Which we can look at as simply extracting the last column of the matrix. In pseudo-code:
Vector3 translation = Vector3(matrix[0, 3], matrix[1, 3], matrix[2, 3])
The scale can be computed as the length of the basis vectors multiplied by M
:
$$s_x = \Vert M \times \begin{bmatrix}1\\0\\0\\0\end{bmatrix} \Vert, \quad
s_y = \Vert M \times \begin{bmatrix}0\\1\\0\\0\end{bmatrix} \Vert, \quad
s_z = \Vert M \times \begin{bmatrix}0\\0\\1\\0\end{bmatrix} \Vert$$
Which is just the length of each of the first three columns interpreted as vectors. In pseudo-code:
Vector3 scale;
for(int i = 0; i < 3; i++) {
scale[i] = sqrt(
matrix[i, 0] * matrix[i, 0]
+ matrix[i, 1] * matrix[i, 1]
+ matrix[i, 2] * matrix[i, 2]);
}
If your scales can be negative then you have an added complication to solve. A reflection in any one axis is identical to a reflection in a different axis combined with a suitable rotation, and a reflection in two axes is equivalent to a pure rotation.
So, you'll need to pick a convention for resolving this. One is to detect whether there is a net reflection (ie. a reflection along 1 or 3 axes) using the determinant/triple-scalar product and decide by fiat that it should be represented by a reflection along the x axis:
if dot(
cross( Vector3(matrix[0,0], matrix[1,0], matrix[2,0]),
Vector3(matrix[0,1], matrix[1,1], matrix[2,1])),
Vector3(matrix[0,2], matrix[1,2], matrix[2,2])
) < 0)
then
sx *= -1
(if sx
or any other scale is zero then you're dealing with a planar, line, or point projection, so we can express it as a rotation without reflection)
With \$S\$ in hand we can divide it out to reconstruct an appropriate \$R\$:
$$R=\begin{bmatrix}
\frac{m_{00}}{s_x} & \frac{m_{01}}{s_y} & \frac{m_{03}}{s_z} & 0 \\
\frac{m_{10}}{s_x} & \frac{m_{11}}{s_y} & \frac{m_{13}}{s_z} & 0 \\
\frac{m_{20}}{s_x} & \frac{m_{21}}{s_y} & \frac{m_{23}}{s_z} & 0 \\
0 & 0 & 0 & 1 \\
\end{bmatrix}$$
Note that if one of \$s_x\$, \$s_y\$, or \$s_z\$ is zero, you'll want to replace the corresponding column with the cross product of the other two to avoid a divide by zero error.
If two axes have zero scale, you can pick two arbitrary unit vectors mutually orthogonal with the remaining axis. If all three axes have zero scale, you can let the rotation matrix be the identity matrix, \$R = I\$, since a point is a point regardless of rotation.
By construction \$R\$ should be an orthonormal rotation matrix, but in practice numerical errors can cause it to deviate, so including an error-correction step where you orthonormalize it is probably a sensible precaution.
You can use the usual methods to convert that rotation matrix into an equivalent unit quaternion or Euler angle triplet if needed, so I won't elaborate on that here.
XMMatrixDecompose
method can recover the elements, but they will not exactly match your original inputs to sayXMMatrixTransformation
because there are many ways to achieve the same rotation. \$\endgroup\$