How can I rotate an object (in 3d) relative to the viewport?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By alann

I want the user to be able to rotate an object in any direction by clicking and dragging.

I have managed to get a mouse vector of the move that tells me the direction the user dragged:

extends MeshInstance

var mouse_start = Vector2(0,0)
var mouse_end = Vector2(0,0)
var dragging = false
var speed = 0.5

func _input(e):
	if e is InputEventMouseButton and e.is_pressed():
		mouse_start = e.position
		dragging = true
	if e is InputEventMouseButton and not e.is_pressed():
		mouse_start = Vector2(0,0)
		mouse_end = Vector2(0,0)
		dragging = false
	if dragging:
		mouse_end = e.position
func _process(delta):
	if dragging:
		var mouse_dir = Vector2(mouse_end.x - mouse_start.x, -mouse_end.y - -mouse_start.y)
		mouse_dir = Vector3(mouse_dir.x, mouse_dir.y, 0).normalized()
		#how to apply relative to camera?

But not sure how to apply this to the rotation correctly so it’s relative to the camera?

I have tried to rotate(mouse_dir, delta * speed), but the cube disappears, not sure why (it prints it’s in the same place, same scale, same rotation)? I get the following error. But using a something like Vector3(0,1,0) or even a Vector3 with lots of decimals like the mouse_dir, normalized, all work? Could this be a bug?

     Condition ' p_axis.is_normalized() == false ' is true.
:bust_in_silhouette: Reply From: SIsilicon

What you could do is use the current camera’s basis vectors as axis of rotation. Specifically it’s local x and y axis.

var cam = get_tree().get_root().get_camera()
var x_axis = cam.get_camera_transform().basis[0]
var y_axis = cam.get_camera_transform().basis[1]

Then all you need to do is rotate about them.

#the bigger variable speed is, the faster it'll rotate.
rotate(y_axis, mouse_dir.x*speed)
rotate(x_axis, mouse_dir.y*speed)

However, you may want to remove this line of code.

mouse_dir = Vector3(mouse_dir.x, mouse_dir.y, 0).normalized()

Now I only made this up on the top of my head so you may want to test this out yourself.