Godot: Easier use of different rotation orders

Created on 22 Jul 2017  Â·  14Comments  Â·  Source: godotengine/godot

Godot, by default, applies the rotation transformations to Spatial in xyz order.

For some applications, however, it can be desirable to apply the rotations in a different order. A common example is that of a camera in a video game, where you would want to apply y before x if you wish the camera to remain horizontal.

Godot doesn't currently make this easy. Currently I manually keep track of the rotation I want in a variable, then re-apply the rotations to the camera appropriately when they change, i.e.:

extends Spatial

var rotation = get_rotation()

# …

def something_changing_rotation():
    rotation.x += 1/100
    update_rotation()

# …

def update_rotation():
    set_rotation(Vector3(0, 0, 0))
    rotate(Vector3(0, -1, 0), rotation.y)
    rotate(Vector3(-1, 0, 0), rotation.x)
    rotate(Vector3(0, 0, -1), rotation.z)

Perhaps Godot could do what THREE.js does, and let you change the rotation order (this might look like .set_rotation_order(Spatial.ROTATION_ORDER_YXZ))? That would work well for the end-user, though I'm not sure if it would cause problems (performance?) for the Godot engine. Or, perhaps it could provide some other facility that avoids this manual handling?

Another workaround is of course to nest your camera or whatever inside another Spatial, and change the y angle on the outer one. That's not as clean, though. (For example, if you were to do this for a scene and instance it in another scene, you wouldn't be able to adjust the full rotation without turning on Editable Children.)

archived discussion feature proposal

Most helpful comment

So you do have something that works like a gimbal, corresponding to YXZ order.
In general, you can convert between any convention of Euler angles to axis-angle, and this would help with precision as every additional rotation introduces some (small) error, but I don't know how slow this will be in GDScript. You're probably better off using 3 consecutive rotations.

If you have only XY rotations, there's a mathematical trick that might help: R_y(β).R_x(α) = (R_x(-α).R_y(-β))ᵀ where superscript T is transposition.

All 14 comments

Ping @tagcup, who is more knowledgeable about such things, and can estimate the difficulty of adding such options. 😃

Adding this in the core shouldn't be hard but there are infinitely many different conventions though, so I wouldn't know how to pick a convention.

Also, the best way to rotate objects is to use axis-angle parametrization. Euler angles isn't really a proper way to do this because it actually corresponds to a different topology than the surface of a 3D sphere.

I can't imagine Euler angles being much useful anything other than setting angles in the editor by hand quick-n-dirty, or a situation where you actually have something that works like a gimbal. Can you tell me a little more about what you're trying to achieve?

@tagcup The specific reason I wanted it is that I have a camera which I want the user to be able to control by changing the X and Y rotations, i.e. your typical video game mouselook on two axes. However, if we apply those rotations in XY order, the camera can quickly cease to be level with the horizon, because each rotation is relative to the previously-applied rotation. In YX order, the camera is always level with the horizon, which is what I am used to video game cameras doing.

There might be a better way to achieve that than applying rotations in different orders. I'm not hugely knowledgeable about the subject.

So you do have something that works like a gimbal, corresponding to YXZ order.
In general, you can convert between any convention of Euler angles to axis-angle, and this would help with precision as every additional rotation introduces some (small) error, but I don't know how slow this will be in GDScript. You're probably better off using 3 consecutive rotations.

If you have only XY rotations, there's a mathematical trick that might help: R_y(β).R_x(α) = (R_x(-α).R_y(-β))ᵀ where superscript T is transposition.

@hikari-no-yume I need a clarification about your first post. You said

you would want to apply y before x if you wish the camera to remain horizontal.

but it is already the case currently. The current convention, XYZ, means Z-first, Y-then, X-last. It's read backwards because it's named after how the sequence of rotation matrices are written.

We're talking about changing the convention used for Euler angles in #1479 and I'd like to get it right. I'm planning to adapt Unity's YXZ (Z-first, X-then, Y-last) convention.

@tagcup Remember to re-add or keep my hack to make all x, y and z range from -180 x 180 :P

Ah, OK :D

@tagcup …maybe I'm getting things confused, then? I don't know. What I do know is that

rotate(Vector3(0, -1, 0), rotation.y)
rotate(Vector3(-1, 0, 0), rotation.x)

gives the desired behaviour, and the default, whatever it is, doesn't.

I don't know what the desired behavior here is but unless you want to rotate it around -x and -y axes, you don't need those minus signs.

Here's a quick test:

    var b1 = Basis()
    b1.set_rotation_euler(Vector3(deg2rad(60),deg2rad(30),0))
    print("b1: ", b1)

    var b2 = Basis()
    b2 = b2.rotated(Vector3(0, 1, 0), deg2rad(30))
    b2 = b2.rotated(Vector3(1, 0, 0), deg2rad(60))
    print("b2: ", b2)

which gives

b1: ((0.866025, 0, 0.5), (0.433013, 0.5, -0.75), (-0.25, 0.866025, 0.433013))
b2: ((0.866025, 0, 0.5), (0.433013, 0.5, -0.75), (-0.25, 0.866025, 0.433013))

as expected.

Ah, were you by any chance using 2.x? The rotations had a sign problem but that's fixed in 3.0.

I was indeed using 2.x. I've so far avoided 3.0 given its immaturity and 2.x being entirely sufficient for my needs.

I think this issue can be closed. Godot uses YXZ in its rotation methods since 3.0 thanks to this commit (see here and here), and using XYZ or other orders is not a very common use case, so exposing XYZ separately would mostly just confuse people.

I'll close this issue per @aaronfranke's comment.

Was this page helpful?
0 / 5 - 0 ratings