Character in 3D rotates towards world Z after jumping

v4.5.stable

So I’m an issue with my character model rotating towards the Z axis after jumping every time.
I currently have setup a FPP and a TPP and this is only occurring in the TPP. I have linked a video of what is occurring. If I disable the lines of code that allow the character to rotate in the direction of movement this issue goes away but then obviously it will no longer rotate. Any and all help is appreciated. Also sorry if the code isn’t posted correctly, I’m new to the forums..

Video: https://youtu.be/0a9B9jAYFA8

extends CharacterBody3D

#nodes
@export_group("Nodes")

#Character root node.
@export var character : CharacterBody3D

#Head node.
@export var head : Node3D

#Settings.
@export_group("Settings")

#Mouse settings.
@export_subgroup("Mouse settings")

#mouse sensitivity.
@export_range(1, 100, 1) var mouse_sensitivity: int = 50

#pitch clamp settings.
@export_subgroup("Clamp settings")

#max pitch in degrees.
@export var max_pitch : float = 60

#min pitch in degrees.
@export var min_pitch : float = -89

@onready var pivot = $CamOrigin
@onready var pivot2 = $CamOrigin/Node3D
@onready var body = $CollisionShape3D
@onready var FPP = $CollisionShape3D/MeshInstance3D/Head/FPP
@onready var TPP = $CamOrigin/Node3D/SpringArm3D/TPP

@export var TPPSense = .01

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var speed = 5
var jump_height = 5
var jump_speed = 1.5

func _ready():
	Input.set_use_accumulated_input(false)
	pivot.set_as_top_level(true)

#Game pause + close
func _unhandled_input(event)->void:
	if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
		if event is InputEventKey:
			if event.is_action_pressed("ui_cancel"):
				get_tree().quit()

		if event is InputEventMouseButton:
			if event.button_index == 1:
				Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

		return

	if event is InputEventKey:
		if event.is_action_pressed("ui_cancel"):
			Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

		return

	if FPP.is_current() and event is InputEventMouseMotion:
		aim_look(event)

#Handles aim look with the mouse.
func aim_look(event: InputEventMouseMotion)-> void:
		var viewport_transform: Transform2D = get_tree().root.get_final_transform()
		var motion: Vector2 = event.xformed_by(viewport_transform).relative
		var degrees_per_unit: float = 0.001
	
		motion *= mouse_sensitivity
		motion *= degrees_per_unit
	
		add_yaw(motion.x)
		add_pitch(motion.y)
		clamp_pitch()

#Rotates the character around the local Y axis by a given amount (In degrees) to achieve yaw.
func add_yaw(amount)->void:
		if is_zero_approx(amount):
			return

		character.rotate_object_local(Vector3.DOWN, deg_to_rad(amount))
		character.orthonormalize()

#Rotates the head around the local x axis by a given amount (In degrees) to achieve pitch.
func add_pitch(amount)->void:
		if is_zero_approx(amount):
			return

		head.rotate_object_local(Vector3.LEFT, deg_to_rad(amount))
		head.orthonormalize()

#Clamps the pitch between min_pitch and max_pitch.
func clamp_pitch()->void:
	if head.rotation.x > deg_to_rad(min_pitch) and head.rotation.x < deg_to_rad(max_pitch):
		return

	head.rotation.x = clamp(head.rotation.x, deg_to_rad(min_pitch), deg_to_rad(max_pitch))
	head.orthonormalize()

#Jump + Movement + Gravity
func _physics_process(delta):
	camera_follows_player()
	velocity.y += -gravity * delta

	move_and_slide()
	if is_on_floor() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and Input.is_action_just_pressed("jump"):
		velocity.y = jump_height

	if FPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and velocity.y != 0:
		var input = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
		var movement_dir = transform.basis * Vector3(input.x, 0, input.y)
		velocity.x = movement_dir.x * speed  * jump_speed
		velocity.z = movement_dir.z * speed  * jump_speed
	elif FPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and velocity.y == 0:
		var input = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
		var movement_dir = transform.basis * Vector3(input.x, 0, input.y)
		velocity.x = movement_dir.x * speed
		velocity.z = movement_dir.z * speed
		
	if TPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and velocity.y != 0:
		var input = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
		var movement_dir = pivot.global_basis * Vector3(input.x, 0, input.y)
		velocity.x = movement_dir.x * speed * jump_speed
		velocity.z = movement_dir.z * speed * jump_speed
	elif TPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and velocity.y == 0:
		var input = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
		var movement_dir = pivot.global_basis * Vector3(input.x, 0, input.y)
		velocity.x = movement_dir.x * speed
		velocity.z = movement_dir.z * speed

	if TPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and velocity.length() > 0:
		character.rotation.y = atan2(-velocity.x,-velocity.z)
		character.orthonormalize()

func camera_follows_player():
	var player_pos = global_transform.origin
	pivot.global_transform.origin = Vector3(player_pos.x, player_pos.y + 2, player_pos.z)
	pivot.orthonormalize()

#TPP camera controls + camera swap
func _input(event):
	if TPP.is_current() and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and event is InputEventMouseMotion:
		pivot.rotate_object_local(Vector3.DOWN, event.relative.x * TPPSense)
		pivot.orthonormalize()
		pivot2.rotate_object_local(Vector3.LEFT, -event.relative.y * TPPSense)
		pivot2.rotation.x = clamp(pivot2.rotation.x, deg_to_rad(-75.0), deg_to_rad(25))
		pivot2.orthonormalize()
		

	if Input.is_action_pressed("switch_perspective"):
		if FPP.is_current():
			FPP.clear_current(true)
		elif TPP.is_current():
			TPP.clear_current(true)

velocity.length() > 0 is true while your jumping so this code will run and set your rotation.y to 0.