This way of representing 3D rotations was groundbreaking at the time, but it has several shortcomings when used in game development (which is to be expected from a guy with a funny
hat).
The idea of this document is to explain why, as well as outlining best practices for dealing with transforms when programming 3D games.
Problems of Euler angles
------------------------
While it may seem intuitive that each axis has a rotation, the truth is that it's just not practical.
Axis order
==========
The main reason for this is that there isn't a *unique* way to construct an orientation from the angles. There isn't a standard mathematical function that
takes all the angles together and produces an actual 3D rotation. The only way an orientation can be produced from angles is to rotate the object angle
by angle, in an *arbitrary order*.
This could be done by first rotating in *X*, then *Y* and then in *Z*. Alternatively, you could first rotate in *Y*, then in *Z* and finally in *X*. Anything works,
but depending on the order, the final orientation of the object will *not necessarily be the same*. Indeed, this means that there are several ways to construct an orientation
from 3 different angles, depending on *the order of the rotations*.
Following is a visualization of rotation axes (in X, Y, Z order) in a gimbal (from Wikipedia). As you can see, the orientation of each axis depends on the rotation of the previous one:
You may be wondering how this affects you. Let's look at a practical example:
Imagine you are working on a first-person controller (e.g. an FPS game). Moving the mouse left and right controls your view angle parallel to the ground, while moving it up and down moves the player's view up and down.
In this case to achieve the desired effect, rotation must be applied first in the *Y* axis ("up" in this case, since Godot uses a "Y-Up" orientation), followed by rotation in the *X* axis.
Depending on the type of game or effect desired, the order in which you want axis rotations to be applied may differ. Therefore, applying rotations in X, Y, and Z is not enough: you also need a *rotation order*.
Interpolation
=============
Another problem with using Euler angles is interpolation. Imagine you want to transition between two different camera or enemy positions (including rotations). One logical way to approach this is to interpolate the angles from one position to the next. One would expect it to look like this:
* Rotations don't map linearly to orientation, so interpolating them does not always result in the shortest path (i.e., to go from `270` to `0` degrees is not the same as going from `270` to `360`, even though the angles are equivalent).
* Gimbal lock is at play (first and last rotated axis align, so a degree of freedom is lost). See `Wikipedia's page on Gimbal Lock ( https://en.wikipedia.org/wiki/Gimbal_lock )` for a detailed explanation of this problem.
The result of all this is that you should **not use** the `rotation` property of `Spatial` nodes in Godot for games. It's there to be used mainly in the editor, for coherence with the 2D engine, and for simple rotations (generally just one axis, or even two in limited cases). As much as you may be tempted, don't use it.
Godot uses the `Transform` datatype for orientations. Each `Spatial` node contains a `transform` property which is relative to the parent's transform, if the parent is a Spatial-derived type.
A transform has a `Basis` (transform.basis sub-property), which consists of three `Vector3` vectors. These are accessed via the `transform.basis` property and can be accessed directly by `transform.basis.x`, `transform.basis.y`, and `transform.basis.z`. Each vector points in the direction its axis has been rotated, so they effectively describe the node's total rotation. The scale (as long as it's uniform) can also be inferred from the length of the axes. A *basis* can also be interpreted as a 3x3 matrix and used as `transform.basis[x][y]`.
Together with the *basis*, a transform also has an *origin*. This is a *Vector3* specifying how far away from the actual origin `(0, 0, 0)` this transform is. Combining the *basis* with the *origin*, a *transform* efficiently represents a unique translation, rotation, and scale in space.
The gizmo's arrows show the `X`, `Y`, and `Z` axes (in red, green, and blue respectively) of the basis, while the gizmo's center is at the object's origin.
Doing successive operations on transforms will result in a loss of precision due to floating-point error. This means the scale of each axis may no longer be exactly `1.0`, and they may not be exactly `90` degrees from each other.
If a transform is rotated every frame, it will eventually start deforming over time. This is unavoidable.
There are two different ways to handle this. The first is to *orthonormalize* the transform after some time (maybe once per frame if you modify it every frame):
It is recommended you not scale nodes that are going to be manipulated; scale their children nodes instead (such as MeshInstance). If you absolutely must scale the node, then re-apply it at the end:
You might be thinking at this point: **"Ok, but how do I get angles from a transform?"**. The answer again is: you don't. You must do your best to stop thinking in angles.
All common behaviors and logic can be done with just vectors.
Setting information
===================
There are, of course, cases where you want to set information to a transform. Imagine a first person controller or orbiting camera. Those are definitely done using angles, because you *do want* the transforms to happen in a specific order.
For such cases, keep the angles and rotations *outside* the transform and set them every frame. Don't try to retrieve and re-use them because the transform is not meant to be used this way.
As you can see, in such cases it's even simpler to keep the rotation outside, then use the transform as the *final* orientation.
Interpolating with quaternions
==============================
Interpolating between two transforms can efficiently be done with quaternions. More information about how quaternions work can be found in other places around the Internet. For practical use, it's enough to understand that pretty much their main use is doing a closest path interpolation. As in, if you have two rotations, a quaternion will smoothly allow interpolation between them using the closest axis.
Converting a rotation to quaternion is straightforward.
can also do transform accumulation, transform points, etc., though this is used
less often). If you interpolate or apply operations to quaternions many times,
keep in mind they need to be eventually normalized. Otherwise, they will also
suffer from numerical precision errors.
Quaternions are useful when doing camera/path/etc. interpolations, as the result will always be correct and smooth.
Transforms are your friend
--------------------------
For most beginners, getting used to working with transforms can take some time. However, once you get used to them, you will appreciate their simplicity and power.
Don't hesitate to ask for help on this topic in any of Godot's `online communities ( https://godotengine.org/community )` and, once you become confident enough, please help others!