Clamping a Basis

Godot Version

4.4

Question

Hello. I’m working on an FPS camera, and I want to use a quaternion for transform.basis. The code works, but I don’t understand at all how do I clamp the X-axis rotation in the basis “x_bas”?

var x_bas = Basis.IDENTITY
var y_bas = Basis.IDENTITY
func camera_look(event: InputEventMouseMotion):
	var mouse_delta:Vector2 = -event.relative * sensitivity
	x_bas *= Basis(Vector3.UP, deg_to_rad(mouse_delta.x))
	y_bas *= Basis(Vector3.RIGHT, deg_to_rad(mouse_delta.y))
	transform.basis = Basis(Quaternion(x_bas.orthonormalized() * Quaternion(y_bas.orthonormalized()))

P.S. I use an online translator, I apologize if my text is written illiterately.

Seems like you shouldn’t be using quaternion mathematics for this axis-bound rotation. Why not just use rotation.x and rotation.y? Godot will end up storing the rotation information in a Basis anyways.

1 Like

I’ve tried different rotation methods. I used rotate and rotate_object_local, and the problem was that “rotation.x = clamp…” was ignored when the mouse was moving vertically quickly. After looking at the answers in this topic and this one, I came to the conclusion that it is better to use quaternions. I do not know how successful and correct my decision is, but so far the camera is moving as it should, except that I do not know how to limit it in the vertical axis.

What I’ve done for that kind of thing is calculate the new position via quaternions, then call get_euler() on it and only apply it if it’s not out of range:

var new_rot = (calculate_new_rotation()) * rotation
var euler = new_rot.get_euler()

if _eulers_within_bounds(euler): rotation = new_rot

Where _eulers_within_bounds() does whatever checking you need for rotation constraints. I’ve put it as a local function for clarity, but in my code it’s just checking that euler.x and euler.z are within +/- (PI * 0.5) inline.

1 Like

So, I found a solution to my original problem.

var x_bas = Basis.IDENTITY
var pitch_angle = 0.0
func camera_look(event: InputEventMouseMotion):
	var mouse_delta: Vector2 = -event.relative * sensitivity
	x_bas *= Basis(Vector3.UP, deg_to_rad(mouse_delta.x))
	pitch_angle = clamp(pitch_angle + mouse_delta.y, -90.0, 90.0)
	var y_bas = Basis(Vector3.RIGHT, deg_to_rad(pitch_angle))
	transform.basis = x_bas * y_bas.orthonormalized()

This will prevent the camera from tilting below or above 90 degrees. And there was really no need to use quaternions, because godot uses them anyway. As I understand it.
Thank you all for the answers!

Yes, it occurred to me, but I didn’t figure out how to implement it. For some reason, I thought that quaternions cannot be converted to Euler angles. Thanks!

No need to have camera rotation be in a basis.
I did that once, and it’s really not fun.