
Matrix definition
A matrix is a grid of numbers, represented by a bold capital letter. The number of rows in a matrix is represented by i; the number of columns is represented by j.
For example, in a 3 X 2 matrix, i would be 3 and j would be 2. This 3 X 2 matrix looks like this:

Matrices can be of any dimension; in video games, we tend to use 2 X 2, 3 X 3, and 4 X 4 matrices. If a matrix has the same number of rows and columns, it is called a square matrix. In this book, we're going to be working mostly with square matrices.
Individual elements of the matrix are indexed with subscripts. For example, refers to the element in row 1, column 2 of the matrix M.
Getting ready
We are going to implement a 2 X 2, 3 X 3, and 4 X 4 matrix. Internally, each matrix will be represented as a linear array of memory. Much like vectors, we will use an anonymous union to support a variety of access patterns. Pay attention to how the indexing operator is overridden, matrix indices in code start at 0, not 1. This can get confusing; when talking about matrices in a non-code context, we start subscripting them with 1, not 0.
How to do it…
Follow these steps to add matrix support to our existing math library:
- Create a new C++ header file, call this file
matrices.h
. Add basic header guards to the file, includevectors.h
:#ifndef _H_MATH_MATRICES_ #define _H_MATH_MATRICES_ #include "vectors.h" // Structure definitions #endif
- Replace the
// Structure definitions
comment with the definition of a 2 X 2 matrix:typedef struct mat2 { union { struct { float _11, _12, _21, _22; }; float asArray[4]; }; inline float* operator[](int i) { return &(asArray[i * 2]); } } mat2;
- After the definition of
mat2
, add the definition for a 3 X 3 matrix:typedef struct mat3 { union { struct { float _11, _12, _13, _21, _22, _23, _31, _32, _33; }; float asArray[9]; }; inline float* operator[](int i) { return &(asArray[i * 3]); } } mat3;
- Finally, after the definition of
mat3
, add the definition for a 4 X 4 matrix:typedef struct mat4 { union { struct { float _11, _12, _13, _14, _21, _22, _23, _24, _31, _32, _33, _34, _41, _42, _43, _44; }; float asArray[16]; }; inline float* operator[](int i) { return &(asArray[i * 4]); } } mat4;
How it works…
In the above code, we implemented 2 X 2, 3 X 3, and 4 X 4 matrices. We used an anonymous union and overloaded the indexing operator to support a variety of access patterns. The usage of anonymous unions is similar to how we constructed the vec2
and vec3
structures.
The underlying data for each matrix is a linear array; rows are laid out sequentially in this array:

This means the matrix is laid out in memory one row at a time, as follows:
float M[9] = { A, B, C, D, E, F, G, H, I };
Each matrix structure supports the following access patterns:
mat4 m4 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 5.0f, 0.0f, 0.0f, 0.0f, 1.0f }; std::cout<< "element at index 11: " <<m4[2][3] << "\n"; std::cout<< "element at index 11: " << m4._34 << "\n"; std::cout<< "element at index 11: " <<m4.asArray[11] << "\n";
The first pattern demonstrated uses the overloaded indexing operator. This operator returns a float pointer to the first element of the specified row. A pointer in C++ can be accessed as an array; this allows us to use double brackets. This overload starts indexing a matrix at 0
.
Next, the anonymous union allows us to access elements using the _ij
notation. Using this notation, i
is the row, j
is the column. These indices start at 1, not 0! This means element [2][3]
is the same as element _34
. This indexing scheme closely resembles the way we talk about math in text.
Finally, we can access the array using the .isArray
member of the anonymous union. This allows us to index the matrix as the underlying linear array structure. Indexing for this array starts at 0. You can convert a 2D array index i,j
, to a 1D array index using the formula: columns * i + j
. Where i
represents the row you are trying to access, j
represents the column, and columns
is the number of columns in the 2D representation of the array.