I know, I know - it' s been keeping you awake too... I've no desire to enhance my well-deserved reputation for being interested in things that could induce narcolepsy in an insomniac, but it took me a fair few hours to get to the bottom of how DirectX manages matrices internally, so in case it's bothering you too, below are the fruits of my labour. Why, you may ask, do I care? Imagine you have an application that allows the user to manipulate objects in 3D, for example applying rotations to them. In your interface, you want to display the current state of play. Your user has applied a number of rotations one after another, perhaps by setting rotations about X, Y & Z, and you want to echo the accumulated effect - you need to be able to turn multiple concatenated rotations into a single set of three rotations about those three axes. In short, you want to unpick the resultant matrix - if I do all these X, Y, Z rotations one after another, what one X,Y,Z rotation would have got me to the same place? Managed DirectX gives us a RollPitchYaw matrix, which, it turns out, builds a matrix which could be represented as Rotation(Z) * Rotation(X) * Rotation(Y). The geometry you remember from school (yeh, right) will remind you that Rotation about X by x radians is:

1 | 0 | 0 |

0 | Cos(x) | Sin(x) |

0 | -Sin(x) | Cos(x) |

while rotation about Y is

Cos(y) | 0 | -Sin(y) |

0 | 1 | 0 |

Sin(y) | 0 | Cos(y) |

and about Z:

Cos(z) | Sin(z) | 0 |

-Sin(z) | Cos(z) | 0 |

0 | 0 | 1 |

so the DirectX Yaw Pitch Roll calculation (z * x * y) looks like this:

Cos(z).Cos(y) + Sin(z).Sin(x).Sin(y) | Sin(z).Cos(x) | Cos(z).-Sin(y) + Sin(z).Sin(x).Cos(y) |

-Sin(z).Cos(y) + Cos(z).Sin(x).Sin(y) | Cos(z).Cos(x) | Sin(z).Sin(y) + Cos(z).Sin(x).Cos(y) |

Cos(x).Sin(y) | -Sin(x) | Cos(x).Cos(y) |

Hence the C# code below for Managed DirectX will allow us to look at a rotation matrix and get back to one set of YawPitchRoll angles that would give rise to it. The DirectX Matrix structure exposes elements called M*ij* (e.g. M32, M21 etc.) where the first digit is the row, and the second the column (remember "Rock Cakes" for **r**ow followed by **c**olumn). This being maths there are gotcha's - when the cosine of the pitch angle gets small, (for a pitch of 90-ish degrees, say) numerically things go bananas, so you can take an arbitrary decision about the yaw angle (here I've set it to zero) and deduce a roll. This is okay except where numerical consistency is important (flying jet fighters. guiding satellites and so on) where you can't just swizzle your object in space to make the sums work. If you're looking for the "proper" way to do this, I can recommend a piece I stumbled on called The Right Way to Calculate Stuff by Don Hatch. If you want the code and layout for the XYZ equivalent, holler - I'll be interested to see whether anyone out there got this far before sleep overwhelmed them.

static public void DecomposeRollPitchYawZXYMatrix(Matrix mx, out double xPitch, out double yYaw, out double zRoll)

{

xPitch = Math.Asin(-mx.M32);

double threshold = 0.001; // Hardcoded constant - burn him, he's a witch

double test = Math.Cos(xPitch);

if(test > threshold) {

zRoll = Math.Atan2(mx.M12, mx.M22);

yYaw = Math.Atan2(mx.M31, mx.M33);

}

else {

zRoll = Math.Atan2(-mx.M21, mx.M11);

yYaw = 0.0;

}

}

Thanks for your patience, O blogosphere - will try to be less turgid in future.

PingBack from http://insomniacuresite.info/story.php?id=819