World Space Getters

So far, we've only represented a transform in world space as a matrix. How can we retrieve the world space position / rotation / scale of a transform? We can retrieve world position and rotatin, we can't retrieve world scale. Unity calls this global scale, lossyScale.

Getting Global Rotation

To get the world rotation of a transform, recursivley multiply the rotation of the transform with it's parents. The code below does this, it uses an iterator instead of recursion.

Quaternion GetGlobalRotation(Transform t) {
    Transform iterator = t.parent

    while (iterator != NULL) {
        rotation = iterator.rotation * t.rotation;
        iterator = iterator.parent
    }

    return rotation;
}

Getting Global Position

The easy way to get the world position of a transform would be to take the last row of the world matrix of the transform. However, we can save a few multiplications by calculating this value without any matrix operations. We need to take the position of the transform and apply it's parents transform to it in the same order as multiplying matrices would:

Vector GetGlobalPosition(Transform t) {
    Vector worldPos = t.position; // Copy, not reference

    Transform iter = t.Parent
    while (iter != null) {
        // First apply parent scale
        worldPos = worldPos * iter.scale // Vec3 * Vec3
        // Next apply parent rotation
        worldPos = worldPos * iter.rotation; // Quat * Vec3
        // Finally apply parent translation
        worldPos += iter.position; // Vec3 + Vec3

        iter = iter.parent
    }

    return worldPos;
}

Getting Global (lossy) Scale

The rotation and scale of a parent transform affects the scale of it's child transforms, this can introduce skewing if a parent has a non-uniform scale. Because of this, retrieving the global scale of a transform is difficult. The first step in doing so is to find a 3x3 Matrix that holds both the rotation and scale of the transform. To find this matrix, convert the scale and rotation of the transform into matrices, combine them and recirsivley multiply with the parent transforms rotation and scale matrix.

Matrix3 GetGlobalRotationAndScale(Transform t) {
    Matrix3 scaleMat = Matrix3(
        t.scale.x, 0, 0,
        0, t.scale.y, 0,
        0, 0, t.scale.z
    );
    Matrix3 rotationMat = ToMatrix(t.rotation);

    Matrix3 worldRS = rotationMat * scaleMat;

    // Recursivley concatenate with parent
    if (t.parent != NULL) {
        Matrix3 parentRS = GetGlobalRotationAndScale(t.parent);
        worldRS = parentRS * worldRS;
    }

    // Return scale rotation
    return worldRS
}

Now that we know the global rotation and scale of the transform, we can remove the rotation component, leaving us with just the scale and skew matrix. To do this, find just the global rotation of the transform, invert that quaternion and turn it into a matrix. This new matrix is the inverse global rotation matrix of the transform. Multiply it with the scale and rotation matrix to remove the rotation component. The main diagonal of the resulting scale and skew matrix is the global lossy scale of the transform.

Vector3 GetGlobalLossyScale(Transform t) {
    // Find inverse global rotation (rotation only) of transform
    Quaternion rotation = GetGlobalRotation(t);
    Matrix3 invRotation = ToMatrix(Inverse(rotation));

    // Find global rotation and scale of transform
    Matrix3 scaleAndRotation = GetGlobalRotationAndScale(t);

    // Remove global rotation from rotation & scale
    Matrix3 scaleAndSkew = invRotation * scaleAndRotation; // Mat3 * Mat3

    // Return the main doagonal of the scale & skew matrix
    return Vector3(scaleAndSkew[0], scaleAndSkew[4], scaleAndSkew[8]);
}

This method of retrieving global scale is covered in more detail in GPU Pro 5, Managing Transformations in Hierarchy by Bartosz Chodorowski and Wojciech Sterna