affine transforms

Header: cglm/affine.h

Initialize Transform Matrices

Functions with _make prefix expect you don’t have a matrix and they create a matrix for you. You don’t need to pass identity matrix.

But other functions expect you have a matrix and you want to transform them. If you didn’t have any existing matrix you have to initialize matrix to identity before sending to transfrom functions.

There are also functions to decompose transform matrix. These functions can’t decompose matrix after projected.

Rotation Center

Rotating functions uses origin as rotation center (pivot/anchor point), since scale factors are stored in rotation matrix, same may also true for scalling. cglm provides some functions for rotating around at given point e.g. glm_rotate_at, glm_quat_rotate_at. Use them or follow next section for algorihm (“Rotate or Scale around specific Point (Pivot Point / Anchor Point)”).

Rotate or Scale around specific Point (Anchor Point)

If you want to rotate model around arbibtrary point follow these steps:

  1. Move model from pivot point to origin: translate(-pivot.x, -pivot.y, -pivot.z)
  2. Apply rotation (or scaling maybe)
  3. Move model back from origin to pivot (reverse of step-1): translate(pivot.x, pivot.y, pivot.z)

glm_rotate_at, glm_quat_rotate_at and their helper functions works that way.

The implementation would be:

1
2
3
glm_translate(m, pivot);
glm_rotate(m, angle, axis);
glm_translate(m, pivotInv); /* pivotInv = -pivot */

Transforms Order

It is important to understand this part especially if you call transform functions multiple times

glm_translate, glm_rotate, glm_scale and glm_quat_rotate and their helpers functions works like this (cglm may provide reverse order too as alternative in the future):

1
2
3
TransformMatrix = TransformMatrix * TraslateMatrix; // glm_translate()
TransformMatrix = TransformMatrix * RotateMatrix;   // glm_rotate(), glm_quat_rotate()
TransformMatrix = TransformMatrix * ScaleMatrix;    // glm_scale()

As you can see it is multipled as right matrix. For instance what will happen if you call glm_translate twice?

1
2
3
glm_translate(transform, translate1); /* transform = transform * translate1 */
glm_translate(transform, translate2); /* transform = transform * translate2 */
glm_rotate(transform, angle, axis)    /* transform = transform * rotation   */

Now lets try to understand this:

1. You call translate using translate1 and you expect it will be first transform because you call it first, do you?

Result will be `transform = transform * translate1`

  1. Then you call translate using translate2 and you expect it will be second transform?

Result will be `transform = transform * translate2`. Now lets expand transform, it was transform * translate1 before second call.

Now it is `transform = transform * translate1 * translate2`, now do you understand what I say?

  1. After last call transform will be:

`transform = transform * translate1 * translate2 * rotation`

The order will be; rotation will be applied first, then translate2 then translate1

It is all about matrix multiplication order. It is similar to MVP matrix: MVP = Projection * View * Model, model will be applied first, then view then projection.

Confused?

In the end the last function call applied first in shaders.

As alternative way, you can create transform matrices individually then combine manually, but don’t forget that glm_translate, glm_rotate, glm_scale… are optimized and should be faster (an smaller assembly output) than manual multiplication

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
mat4 transform1, transform2, transform3, finalTransform;

glm_translate_make(transform1, translate1);
glm_translate_make(transform2, translate2);
glm_rotate_make(transform3, angle, axis);

/* first apply transform1, then transform2, thentransform3 */
glm_mat4_mulN((mat4 *[]){&transform3, &transform2, &transform1}, 3, finalTransform);

/* if you don't want to use mulN, same as above */
glm_mat4_mul(transform3, transform2, finalTransform);
glm_mat4_mul(finalTransform, transform1, finalTransform);

Now transform1 will be applied first, then transform2 then transform3

Functions documentation

void glm_translate_to(mat4 m, vec3 v, mat4 dest)

translate existing transform matrix by v vector and store result in dest

Parameters:
[in] m affine transfrom
[in] v translate vector [x, y, z]
[out] dest translated matrix
void glm_translate(mat4 m, vec3 v)

translate existing transform matrix by v vector and stores result in same matrix

Parameters:
[in, out] m affine transfrom
[in] v translate vector [x, y, z]
void glm_translate_x(mat4 m, float x)

translate existing transform matrix by x factor

Parameters:
[in, out] m affine transfrom
[in] v x factor
void glm_translate_y(mat4 m, float y)

translate existing transform matrix by y factor

Parameters:
[in, out] m affine transfrom
[in] v y factor
void glm_translate_z(mat4 m, float z)

translate existing transform matrix by z factor

Parameters:
[in, out] m affine transfrom
[in] v z factor
void glm_translate_make(mat4 m, vec3 v)

creates NEW translate transform matrix by v vector.

Parameters:
[in, out] m affine transfrom
[in] v translate vector [x, y, z]
void glm_scale_to(mat4 m, vec3 v, mat4 dest)

scale existing transform matrix by v vector and store result in dest

Parameters:
[in] m affine transfrom
[in] v scale vector [x, y, z]
[out] dest scaled matrix
void glm_scale_make(mat4 m, vec3 v)

creates NEW scale matrix by v vector

Parameters:
[out] m affine transfrom
[in] v scale vector [x, y, z]
void glm_scale(mat4 m, vec3 v)

scales existing transform matrix by v vector and stores result in same matrix

Parameters:
[in, out] m affine transfrom
[in] v scale vector [x, y, z]
void glm_scale_uni(mat4 m, float s)

applies uniform scale to existing transform matrix v = [s, s, s] and stores result in same matrix

Parameters:
[in, out] m affine transfrom
[in] v scale factor
void glm_rotate_x(mat4 m, float angle, mat4 dest)

rotate existing transform matrix around X axis by angle and store result in dest

Parameters:
[in] m affine transfrom
[in] angle angle (radians)
[out] dest rotated matrix
void glm_rotate_y(mat4 m, float angle, mat4 dest)

rotate existing transform matrix around Y axis by angle and store result in dest

Parameters:
[in] m affine transfrom
[in] angle angle (radians)
[out] dest rotated matrix
void glm_rotate_z(mat4 m, float angle, mat4 dest)

rotate existing transform matrix around Z axis by angle and store result in dest

Parameters:
[in] m affine transfrom
[in] angle angle (radians)
[out] dest rotated matrix
void glm_rotate_make(mat4 m, float angle, vec3 axis)

creates NEW rotation matrix by angle and axis, axis will be normalized so you don’t need to normalize it

Parameters:
[out] m affine transfrom
[in] axis angle (radians)
[in] axis axis
void glm_rotate(mat4 m, float angle, vec3 axis)

rotate existing transform matrix around Z axis by angle and axis

Parameters:
[in, out] m affine transfrom
[in] angle angle (radians)
[in] axis axis
void glm_rotate_at(mat4 m, vec3 pivot, float angle, vec3 axis)

rotate existing transform around given axis by angle at given pivot point (rotation center)

Parameters:
[in, out] m affine transfrom
[in] pivot pivot, anchor point, rotation center
[in] angle angle (radians)
[in] axis axis
void glm_rotate_atm(mat4 m, vec3 pivot, float angle, vec3 axis)
creates NEW rotation matrix by angle and axis at given point
this creates rotation matrix, it assumes you don’t have a matrix
this should work faster than glm_rotate_at because it reduces one glm_translate.
Parameters:
[in, out] m affine transfrom
[in] pivot pivot, anchor point, rotation center
[in] angle angle (radians)
[in] axis axis
void glm_decompose_scalev(mat4 m, vec3 s)

decompose scale vector

Parameters:
[in] m affine transform
[out] s scale vector (Sx, Sy, Sz)
bool glm_uniscaled(mat4 m)

returns true if matrix is uniform scaled. This is helpful for creating normal matrix.

Parameters:
[in] m matrix
void glm_decompose_rs(mat4 m, mat4 r, vec3 s)

decompose rotation matrix (mat4) and scale vector [Sx, Sy, Sz] DON’T pass projected matrix here

Parameters:
[in] m affine transform
[out] r rotation matrix
[out] s scale matrix
void glm_decompose(mat4 m, vec4 t, mat4 r, vec3 s)

decompose affine transform, TODO: extract shear factors. DON’T pass projected matrix here

Parameters:
[in] m affine transfrom
[out] t translation vector
[out] r rotation matrix (mat4)
[out] s scaling vector [X, Y, Z]