Quaternions (Maths for Game Developers)

Edit on Github | Updated: 1st April 2024

Why Quaternions?

If you have used any 3D editor before then you are probably used to seeing 3D rotation in terms of X,Y,Z degrees of rotation, e.g 45 degrees in X, 90 in Y and 25 in Z. These are called Euler angles and they are fairly easy for the user to understand.

So you would think that when programming games you would just use these Euler angles to rotate your 3d models. But sadly these Euler angles have a big flaw called Gimbal Lock, the solution is to instead use Quaternions to store the rotation information of your 3D model.

Gimbal Lock

Gimbal lock happens when 2 of the plains align on the same axis and results in weird animation in 3D space.

Gimbal Lock and Apollo 13

If you think Gimbal lock only affects game developers then try being an aerospace engineer!


Writing your own Quaternion Library

This is an excellent video which covers how to write your own Quaternion library, it uses Java but the concepts can apply to any programming language.


Unity


Nintendo DS

The Nintendo DS Operating System has a small Quaternion helper library defined in the header file IrisQUAT.h. This file was leaked as part of the September 2020 “Platinum leak” as it is part of the Nintendo DS Boot ROM.

Here are the types it provides to the developer:

// 16-bit
typedef struct {
    s16 x;
    s16 y;
    s16 z;
    s16 w;
} Quat16, Quat;
typedef  vl Quat16    vQuat16;
typedef  vl Quat      vQuat;

// 32-bit
typedef struct {
    s32 x;
    s32 y;
    s32 z;
    s32 w;
} Quat32;
typedef  vl Quat32    vQuat32;

Here are all of the functions it provides:

#define VEC_Conv2Quat(axisp, theta, dstp)     VEC_Conv2QuatPriv(SIN_NDIV_DEFAULT, axisp, theta, dstp)

void VEC_Conv2Quat256(const Vec *axisp, u32 theta, Quat *dstp);
void VEC_Conv2Quat1024(const Vec *axisp, u32 theta, Quat *dstp);
void VEC_Conv2Quat4096(const Vec *axisp, u32 theta, Quat *dstp);

#define VEC_Conv2QuatPriv(ndiv, axisp, theta, dstp)   VEC_Conv2QuatNDiv(ndiv, axisp, theta, dstp)
#define VEC_Conv2QuatNDiv(ndiv, axisp, theta, dstp)   VEC_Conv2Quat##ndiv(    axisp, theta, dstp)


s32  QUAT_DotProduct(const Quat *a, const Quat *b);

void QUAT_Normalize(Quat *srcp, Quat *dstp);

void QUAT_Inverse(Quat *srcp, Quat *dstp);

void QUAT_Multiply(Quat *a, Quat *b, Quat *axb);

void QUAT_Add(Quat *a, Quat *b, Quat *a_b);

void QUAT_Sub(Quat *a, Quat *b, Quat *a_b);

void QUAT_Scale(Quat *srcp, Quat *dstp, s32 scale);

void QUAT_Lerp( Quat *p, Quat *q, Quat *d, s32 t);


void QUAT_Slerp(Quat *p, Quat *q, Quat *d, s32 t);