Transforming models

Beginner Getting started

When a mesh is rendered, each of its vertices goes through a series of matrix multiplications before it finally gets on the screen. These matrices are knows as the world, view and projection matrices. While the view and the projection matrix control where is the camera, which direction is it looking and how is the 3D scene projected onto the screen (i.e. orthographic or perspective projection), the world matrix is the one that moves, rotates and scales the models. In this tutorial we will have a look at how you can transforms models using the world matrix.

Contents

The world matrix

GML comes with a few built-in functions using which you can control mentioned matrices and you can find them in the official manual. For us the most interesting ones are matrix_build, matrix_build_identity, matrix_multiply and matrix_set.

bbmod_material_reset();
matrix_set(matrix_world, matrix_build(x, y, z, 0, 0, direction, 1, 1, 1));
model.submit();
matrix_set(matrix_world, matrix_build_identity());
bbmod_material_reset();

The code above uses matrix_build to create a new matrix that:

  1. rotates the model around the Z axis by the direction variable,
  2. leaves the scale of the model at 1 for each axis,
  3. translates the model by x, y, z variables on the XYZ axes,

all of which in this exact order, then sets it as the current world matrix with matrix_set(matrix_world, ...) and draws a model. Note that because of this order of transformations matrix_build produces skew matrices when non-uniform scaling is used in combination with rotations! If you instead wanted to scale the model first and then rotate it, you would have to do that with two separate matrices multiplied together:

// Only scales model:
var _matrixScale = matrix_build(0, 0, 0, 0, 0, 0, scaleX, scaleY, scaleZ);
// Only rotates and translates model:
var _matrixRotateTranslate = matrix_build(x, y, z, 0, 0, direction, 1, 1, 1);
// Scales first, then rotates and translates:
var _matrixScaleRotateTranslate = matrix_multiply(_matrixScale, _matrixRotateTranslate);
matrix_set(matrix_world, _matrixScaleRotateTranslate);

The last line in our previous example resets the world matrix back to an identity matrix, which is built using the matrix_build_identity function. This matrix leaves vertices untransformed, i.e. the resulting vertex position is the same one as the input vertex position. This is not necessarily required to do after each model is drawn, but it definitely should be done after all models are drawn, otherwise the world matrix could also transform other graphical elements like GUI for example.

Matrix utilities

As you have noticed, the built-in GML functions for dealing with matrices can be quite verbose, especially when doing more complex transformations that require several matrix multiplications. For this reason BBMOD comes with a struct BBMOD_Matrix, which simplifies chained transformations into method calls. Here is how would you rewrite the code above using BBMOD_Matrix:

bbmod_material_reset();
new BBMOD_Matrix()
    .RotateZ(direction)
    .Translate(x, y, z)
    .ApplyWorld();
model.submit();
new BBMOD_Matrix().ApplyWorld();
bbmod_material_reset();

The code became much more self-explanatory, without having to learn in which specific order is translation, rotation and scale executed when you use matrix_build. Not to mention that you do not need to fill in all 9 arguments even in case you just wanted to rotate a model around single axis. For the full list of BBMOD_Matrix methods, please see its documentation.

Performance considerations

Matrix multiplications in GML are an expensive operation and doing a lot of them each frame can drastically affect performance. It is recommended to instead cache the results and reuse them whenever possible! For example, if you have an object that never moves, you can compute its transformation just once in the object's create event and then simply set it in the draw event:

// Some place executed just once, for example inside of a script:
global.matrixIdentity = matrix_build_identity();
/// @desc Create event
matrix = new BBMOD_Matrix()
    .Scale(scaleX, scaleY, scaleZ)
    .RotateZ(direction)
    .Translate(x, y, z);
/// @desc Draw event
bbmod_material_reset();
matrix.ApplyWorld();
model.submit();
matrix_set(matrix_world, global.matrixIdentity);
bbmod_material_reset();

Could not find what you were looking for?

We are still working on more tutorials. If you need additional help with BBMOD, please have a look at the documentation or join our Discord community. Thank you for your patience.

Support the development

Support us in developing BBMOD, get priority assistance and more of our amazing tools as a reward!

Become Patron

Don't miss out on a thing!

Create an account, subscribe to newsletter and get notified on new releases, tutorials and special offers.

Register Log in