
Isner's Guide to Thinking with Transforms and Matrices
Basic Qualities
Visualizing 4*4 Matricies
Table of Matrix Multiplication
Inverting Matricies
Introduction to Transforms
Visualizing Classical
Scaling Transforms
VIsualizing Softimage
Scaling Transforms
Local + Global Transforms
Inverting Softimage Scaling
When it comes right down to it, there are only a couple things going on in 3D:
transformations moving and rotating about, vectors and planes
projecting/intersecting, and the occasional interpolation algorithm like a spline
or patch. Like Physics, at a low level there's a set of fundamental
interacting components and everything else sits on top of that.
Definitely a movie like The Matrix really plays on the
idea that these building blocks are a core component in this new world of human
imagination we're constructing. Seeing how these "atoms" or building
blocks shift around and interact has many uses, especially for for solving problems or
creating low-level machines for achieving 3D tasks.
For me, visualizing vectors always came easy. It's pretty direct to
understand manipulating little lines with arrows. Rotations and
quaternions came harder, but I had no choice because I was banging my head up
against problems that required them so frequently. But transformations
have remained a little more elusive. Sure most character setup problems
come down to transform issues in the end, but 3D software and even programming
API's are very helpful in letting you productively get by with only a general
sense of what you are doing with them.
But like with many problems, to get to a higher level you need to understand the depth of a lower level and I've decided it's time to expose the details of these atoms for visual people..
When I started this project I found all of the discussion on matrices and transforms to date revolves around the mathematics or math textbook type diagrams which wasn't giving me a clear picture of the underlying mechanism in my head. So what I've tried to do in this document is to invent a language to see matrix + transform manipulation that covers the whole range of behavior, and gives a framework to think and invent with.
Ok, so let's start with the basics. A transformation is a container that holds 3 things: position, rotation and scale.
Lets start with a single object A in the space of the origin O:

The most common way of thinking of transforms is how I phrased it above: one object sits in the space of another.
In this picture Object A sits in global space. That means the coordinates on the right describe how to get from the origin to it's current state.
Simple enough.
But if we wanted to go into more detail, how can you picture one object being in the space
of another as a process or set of instructions?
Well the starting point is understanding the root computation behind transforms: 4 * 4 matrix manipulation.
Let's start piecing together the components that happen in 3D matrix computation. There are a few aspects of why 4*4 matrix are useful for 3D.
The first thing you could say is that can be used to hold Scale, Rotation and Translation:

As a starting point, you could consider all three of these components are an offset. So a
rotation of 30, starts at 0 and goes to 30. A translation of 6,2 starts at
(0,0) and goes to (6,2).
The significant thing about 4*4 Matrix manipulation is the way these intertwine
together.
If you take a position and rotation difference together the rotation offset occurs across the position change.

A good way to visualize it is as a rotating disk or coordinate system on the end of a stick. It's exactly this quality that makes matrices it useful for doing transform manipulation. You can think of the rotations as "binding points" for little molecules that create hierarchical motion. Characters, and other hierarchically driven 3D machines are all composed of sets of these sticks acting as the offsets between parents and children.
So the next thing to take into account is scale. You can also picture a scaling delta happening in a similar manner to rotation and the start and end of a stick:

When you bring together rotation and scaling the visual result is predictable:

However there's a twist. With scaling the behavior
is a bit different than a simple delta that starts at zero and becomes your
values at the end of the stick.
Matrix scaling acts more like there are two independent sets of scaling values.
The first sits on the grid at the origin, the second at the end of the stick.
What complicates matrix manipulation is that scaling the grid at the origin
shears everything else, including rotation values and the second grid. So
in the above result, if you scale the x axis on the first grid 160%, the space (ie
grid icon) at the end of the stick no longer has its x + y axis perpendicular to
one another.
In the following example, the shearing is coming from the fact that scaling is applied after the translation and rotation. You could get this result from the transformation order (rotation, scale, translate) or by having a parent with non-uniform scaling.

Keep in mind in this sheared space rotation values are also warped. Shearing will only occur with non-uniform scaling. So in the picture above if you scaled both the x + y axis 160% there would be no shearing, just an increase in the length of the stick and the scale of the second space.
So now that we have roughed out a language to "see" different states of a matrix, how can you become familiar with with how to manipulate it? I believe the quickest route to true familiarity is having a perspective on the range of possibility. So in the case of matrices, the first range to look at is multiplication
Table of Matrix Multiplication
Below is a table of the different possible combinations of
multiplying matrices together.
Both columns and rows start from an empty matrix and become more complex
throug h: Translation, Rotation, Scaling, Rot+Trans, SRT (non-uniform).
The Multiplication results from B2 to F6 can be clicked on for a larger diagram with a Jscript example for XSI.
One of the first things that should be obvious looking at this table is that matrix multiplication is non-commutative (A*B don't equal B*A). Multiplying a Translation Only by a Rotation Only in B3 is not the same as multiplying a Rotation Only by a Translation Only in C2.
Looking at a result like E5, you can see is exactly a parenting relationship, where E1 is the child of A5.
So next let's look at the other important manipulation you can do with a Matrix: inversion.
Inverting a Matrix is a little more complex to visualize than negating a vector and getting it to point in the opposite direction.
An inverse is an opposite like you would expect, but you
have to have a clear picture in your mind of what a matrix is doing to
understand the inversion.
For a uniform scaling inversion, it's pretty straightforward. If you go
back to the stick metaphor, just picture instead of going from the origin of the
stick towards a goal, you go backwards from the goal of the stick to the origin.

You can see when rotation is non-zero, the translation numbers are not going to be the same after inversion because the coordinate system you are describing the path back with is that of the goal.
In the case of a non-uniform scaled matrix, it's a bit
more tricky. You need to:
1) Picture going back from the orientation of the goal
towards the origin.
2) Get rid of scaling on the goal by multiplying it by the inverse of it's
scaling.
The Inverse Matrix will have shearing on the goal space (ie grid and rotation).

So multiplying Matrix A- by Matrix A will produce an empty matrix.
Inverting through a sheared non-uniform matrix is similar,
but has the added twist that the result does not scale the first grid back to a
uniform one.
You are scaling back to the inverse of the scaling only, and that results in a
non-uniformly scaled (but not sheared) starting grid on the inverse.
Basically if you have scaling on the origin grid (which is what introduces shearing) then we need to add two add two new steps to the visual instructions to also invert that origin scale.

To make the origin scaling a little more clear, the following animation (in .avi format) demonstrates the difference between a Matrix and it's inverses as the x-axis scaling increases at the origin.
So you can see things get a bit tricky when you have non-uniform scaling on the different axes, but once you figure out how it works, it really reinforces the view of a 4*4 matrix as rotation on a stick between these two grid spaces.
Because you have two scale spaces like this, matrices can be used to take an local transform of an object parented below 20 other and convert that into a single global transform which only holds the result from the compounding scale, shear, translation and rotation changes.
So now that we have a very clear idea about what a 4*4 Matrix is and how it can be manipulated let's begin to analyze how transforms and parenting work.
The first thing you have to consider, is that in XSI there are two kinds of transform algorithms. The one that derives simply from the behavior of matrices is called "Classic Scaling". The second, "Softimage Scaling" was created with the purpose of removing shearing from transform inheritance. You can see from the examples above that once that once shearing makes the axes no longer perpendicular, it results in a form of distortion, esp when applied to geometry..
The algorithms produce identical results with uniform scaling, the only difference is how they handle non-uniform scaling.
These different transform types can be applied at an object level and are found in the local Kinematics tab:

If the Scaling Computation > Hiearchical (Softimage) Scaling button is clicked you are using Softimage Scaling. If it is not clicked then you are using Classical scaling. You can also set all newly create objects as classical scaling under User Preferences > Interaction > Tools > Use Classical Scaling for Newly Created Object. But I recommend against using that option unless you are an expert user and have a specific reason for doing so. The reason is it has side effects for animation tools that have a need for continuous rotations like the Skeleton > Spine (ie IsnerSpine).

So let's get right down to the details and look at how these different transform algorithms work.
Visualizing Classical Scaling Transforms
The simpler of the two is classical, controlled by the equation:
Global Matrix of Child = Local Child Scale Matrix * Local Child Rotation Matrix * Local Child Translation Matrix * Global Parent Matrix
even simpler, you can think of that equation of a set of instructions.
1) Fill Value Matrix with the
child transform values in the order: S, R, T
2) Global Matrix of Child =
Value Matrix
* Parent Global Matrix
Now let's look at that as a set of geometric instructions.

Visualizing Softimage Scaling Transforms
So the next algorithm we are going to look at is the
default in XSI, built for the purpose of defeating shearing distortion problems.
First lets start with the algorithm:
Scale Change Matrix = Child
Scale Matrix * Parent Scale Matrix
Rotation Change Matrix = Child Rotation Matrix * Parent
Rotation Matrix
Translation Change Matrix = Child Translation Vector * Parent Matrix (full SRT)
Global Matrix of Child = Scale Change Matrix * Rotation Change Matrix * Translation Vector (Extracted from Translation Change Matrix)
as a simpler set of instructions:
1) Get the difference between the Parent and Child's
scaling
2) Get the difference between the Parent and Child's rotations
3) Multiply the translation vector by the full Parent Matrix ie (place, rotate
and scale the start of the stick in the space of the Parent)
4) Place the Scale and Rotations from step 1 at the end of that vector.
In diagram form:

So you can see the end result is almost identical to Classical Scaling. In fact the result is exactly the same expect for when you have non-uniform scaling. If you ever needed to mimic or break down this behavior yourself in scripting, here's a function that does it. It could probably be better optimized, but I kept it simple for legibility:
/*--------------------------------
Softimage Transform Multiply
--------------------------------*/
function SoftTransMul(inTrans1, inTrans2){
var scale1 = XSIMath.CreateVector3();
var scale2 = XSIMath.CreateVector3();
var rot1 = XSIMath.CreateVector3();
var rot2 = XSIMath.CreateVector3();
var trans1 = XSIMath.CreateVector3();
var scaleChange = XSIMath.CreateVector3();
var rotChange = XSIMath.CreateVector3();
var S1 = XSIMath.CreateTransform();
var R1 = XSIMath.CreateTransform();
var S2 = XSIMath.CreateTransform();
var R2 = XSIMath.CreateTransform();
inTrans1.GetScaling(scale1);
inTrans1.GetRotationXYZAngles(rot1);
S1.SetScaling(scale1);
R1.SetRotationFromXYZAngles(rot1);
inTrans2.GetScaling(scale2);
inTrans2.GetRotationXYZAngles(rot2);
S2.SetScaling(scale2);
R2.SetRotationFromXYZAngles(rot2);
// Get result of multiplying scale + rot
S1.MulInPlace(S2);
R1.MulInPlace(R2);
// Get the position result from the matrices multiplied
inTrans1.GetTranslation(trans1);
trans1.MulByTransformationInPlace(inTrans2);
// Bring it all together
var resultTrans = XSIMath.CreateTransform();
resultTrans.MulInPlace(S1);
resultTrans.MulInPlace(R1);
resultTrans.SetTranslation(trans1);
return resultTrans;
}
//------------------------------------------------------
So the next thing to look at is how these transforms cluster together in hierarchies of objects.
With "classic scaling" transforms it's easy to walk up and down the hierarchy. The example below has an arm with three transforms.

To get the global position at the top of the hierarchy above, multiply B * A, then the result by C. In Jscript:
B_global.Mul (B, A);
C_global.Mul(C,B_global);
Any global position can be obtained by walking up the
local matrices from the bottom up.
Also if you have a global matrix at any point, you can get the global matrix
below it my multiplying it by the inverse of the local position.

Now with "Softimage Scaling", these techniques work only with uniform scaling. To convert from global to local in Softimage scaling, you walk the hierarchy using the algorithm covered in "Visualizing Softimage Scaling Transforms" above.
But if you want to get a bit trickier and walk from a global position one back, you need to figure out the inverse of the Softimage Scaling algorithm.
Inverting Softimage Scaling
Now walking up and down the hierarchy like the previous
example will work exactly the the same with Softimage transforms if you have
uniform scaling.
However in the case of Softimage Scaling we need to come up with a process that
does the same thing as Softimage scaling but backwards down the hierarchy.
To do that:
1) Get a rotation difference between Global and Local (to
Inverse).
2) Get a scaling difference between Global and Local (to Inverse).
3) Get a translation difference between Global and Local (to Inverse) into a
vector.
4) Negate the translation vector and multiply it by a matrix containing the
rotation and scaling difference.
5) Build a matrix with the SRT's obtained from steps 1,2,4.
Using this process you can walk a Softimage scaling hierarchy backwards:

Here's a Jscript function that will achieve the task.
/*--------------------------------
Inverse Multiply Softimage Transform
--------------------------------*/
function InverseMultiply((inINVtrans, inTrans){
var s1 = XSIMath.CreateVector3();
var s2 = XSIMath.CreateVector3();
var rot1 = XSIMath.CreateVector3();
var rot2 = XSIMath.CreateVector3();
inINVtrans.GetScaling(s1);
inINVtrans.GetRotationXYZAngles(rot1);
inTrans.GetScaling(s2);
inTrans.GetRotationXYZAngles(rot2);
// get the deltas
scaleDelta = XSIMath.CreateVector3();
rotDelta = XSIMath.CreateVector3();
scaleDelta.set(s2.x/s1.x,s2.y/s1.y,s2.z/s1.z);
rotDelta.sub(rot2,rot1);
var modifier = XSIMath.CreateTransform();
modifier.SetScaling(scaleDelta);
modifier.SetRotationFromXYZAngles(rotDelta);
//Get the translation
var modVec = XSIMath.CreateVector3();
inINVtrans.GetTranslation(modVec);
modVec.NegateInPlace();
modVec.MulByTransformationInPlace(modifier);
var returnVec = XSIMath.CreateVector3();
inTrans.GetTranslation(returnVec );
returnVec.AddinPlace(modVec);
var returnTrans = XSIMath.CreateTransform();
returnTrans.Copy(inINVtrans);
returnTrans.SetScaling(scaleDelta);
returnTrans.SetRotationFromXYZAngles(rotDelta);
returnTrans.SetTranslation(returnVec)
return returnTrans ;
}
So that's it, there's a million different problems that
can be solved with these basic tools. So good luck in your projects, and I
hoped this serves as a useful introduction or reference.
Also thanks to Peter Shretlen, Réjean Gagné and Brent Mcpherson for chatting
about transforms.