Basis rotation errors when player is flipped (scale.x = -1)

Godot Version

4.2.2

Question

I have a 3D character moving and rotating in a 3D world, but it only moves in 2D, and is rotated only on the Z-axis.
The best way I could figure to add rotations of different degrees, from different sources of input, without them over-writing each other or suddenly snapping, was to transform a target basis value, and each frame .slerp() my character closer to that target basis.
This is some cases returning the error : “basis must be normalized to be cast to quaternion” and player resets to to a flat 0 degree angle.

(sorry - I dont know if this is going to format as a code block or not)
This is the function running every frame:

func basis_to_target_interp():
	player.scale.x = abs(player.scale.x)
	var current_quat = Quaternion(player.basis)
	var target_quat = Quaternion(target_basis)
	var slerp_quat = current_quat.slerp(target_quat, 0.5)
	player.transform.basis = Basis(slerp_quat)
	player.scale.x *= direction

This works great when I am setting the target basis to be the normal of the floor ( to angle the player up slopes), but only If I apply the direction scaling after the transformation.

It also works here, but only when I am facing forward (direction = 1):

    player.basis = \
		player.basis.rotated(Vector3.BACK, (PI/5))
	target_basis = \
		player.basis.rotated(Vector3.BACK, (PI/6 * jump_charge))

This code is meant to snap the basis first to a certain angle, then set a target to rotate too over a few frames.
This code works, but only if “scale.x = 1”, but if its -1, the character does not rotate at all (the quaternion of the target basis returns [0,0,0,1] )

I dont know enough about this subject to know if I am approaching this process wrong, and I have tried slapping .normalized() and .orthonormalized(), on any value I can before and after doing the basis transform, but I am out of ideas.
Any suggestions?

2 Likes

It looks like the issue is related to your character’s scale, especially when scale.x = -1. This can cause problems because a negative scale can invert the basis, leading to non-normalized quaternions and issues with rotation.

Suggestions:
1 Normalizing Basis:

Before converting to a quaternion, normalize the basis to avoid the “basis must be normalized” error:

var current_quat = Quaternion(player.basis.orthonormalized())
var target_quat = Quaternion(target_basis.orthonormalized())

This ensures your basis is properly normalized before slerping.

2 Handling Negative Scale:

Negative scaling affects the handedness of your coordinate system, leading to issues when converting to quaternions. To avoid this, handle rotation separately from scaling:

var temp_basis = player.basis
temp_basis.scale(Vector3(1, 1, abs(player.scale.x)))  # Apply only positive scale for rotation
var current_quat = Quaternion(temp_basis.orthonormalized())
var slerp_quat = current_quat.slerp(target_quat, 0.5)
player.transform.basis = Basis(slerp_quat)
player.scale.x *= direction  # Apply direction scaling after rotation

BUT
If you’re consistently facing issues with negative scales, consider keeping your scale positive and adjusting other properties to achieve the same effect (e.g., flipping your character’s visuals rather than using negative scales).

This should help maintain proper rotation behavior regardless of scale.