Game Physics Cookbook
上QQ阅读APP看书,第一时间看更新

Transform matrix

We've briefly touched on the fact that our math library multiplies matrices in a left to right order. But what exactly does this mean? When we multiply two matrices, we combine their linear transformations into one matrix. The first transformation applied is the one on the far left, then the one to its right, and so on.

For example, let's take two matrices, one that translates an object by 10 units on its X axis and one that rotates it by 45 degrees on its Y axis:

mat4 transform1 = Translate(10, 0, 0) * RotateY(45);
mat4 transform2 = RotateY(45) * Translate(10, 0, 0);

Because matrix multiplication is not cumulative Transform matrix, transform1, and transform2 are not the same! transform1 will move the object to (10, 0, 0), and then rotate the object at that position:

Transform matrix

transform2, on the other hand, will rotate the object by 45 degrees on its Y axis, and then translate it by 10 units on its local X axis:

Transform matrix

Getting ready

The multiplication order is highly dependent on the conventions you are using and what makes sense to you. For the context of our physics engine we want to scale first, rotate second, and translate last. This order is a fairly common convention in most games. We're going to make a helper function that takes scale, rotation, and translation and returns a full transform matrix.

We have two ways to create a rotation matrix, using Euler angles or using the axis angle representation. We are going to implement two versions of the Transform function, one for each representation.

How to do it…

Follow these steps to create a composite matrix given scale, rotation, and translation:

  1. Add the declaration for the Transform function to matrices.h:
    mat4 Transform(const vec3& scale, const vec3& eulerRotation,
       const vec3& translate);
    mat4 Transform(const vec3& scale, constvec3& rotationAxis,
       float rotationAngle, const vec3& translate);
  2. Implement the Euler angle version of the Transform function in matrices.cpp:
    mat4 Transform(const vec3& scale, const vec3& eulerRotation,
       const vec3& translate) {
          return Scale(scale) *
          Rotation(eulerRotation.x, 
                   eulerRotation.y, 
                   eulerRotation.z) *
          Translation(translate);
    }
  3. Implement the axis angle version of the Transform function in matrices.cpp:
    mat4 Transform(const vec3& scale, const vec3& rotationAxis,
       float rotationAngle, const vec3& translate) {
          return Scale(scale) * 
          AxisAngle(rotationAxis, rotationAngle) * 
          Translation(translate);
    }

How it works…

Generally, to create a three-dimensional transform, we want to scale first, rotate second, and translate last. If this order of transformations seems arbitrary, that's because it absolutely is.

We scale first to avoid the scaling matrix interfering with the rotation matrix. Then, we want to rotate before translating. In this way, the position of the object is intuitively where we tell the translation to be. Also, the rotation will happen in the world axis. Because we multiply from left to right, we can express a transformation from left to right:

Transform = Scale(1, 2, 1)      * 
            Rotation(0, 30, 0)  * 
            Translation(10, 0, 0);