Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | Sancarn |
Rotating the camera around a point in 3d space
This is a problem which has been boggling my mind for the past few weeks. I wanted to click on an object in the viewport and drag my mouse around the screen meanwhile rotating around the point in 3d space.
The technique I settled with was normalizing the camera position, converting it into polar coordinates, add a specific angle to yaw and pitch, and then calculating the new x,y,z co-ordinates from the new polar co-ordinates.
I built the functions below to accomplish this:
func rotateV3AroundPoint(toRotate=Vector3(0,0,0), aroundPoint=Vector3(0,0,0), theta=0, phi=0):
#Get normalized cartesian co-ordinates
var p_normalized = toRotate - aroundPoint
#Get p_normalized as polar coordinates
var p_polar = getPolarCoordinates(p_normalized)
#Increment polar coordinates given theta and phi
var np_polar = Vector3(p_polar.x,p_polar.y-theta,p_polar.z-phi)
#Translate new polar to cartesian
var np_normalized = getCartesianCoordinates(np_polar)
#De-normalize polar coordinate
var np = np_normalized + aroundPoint
return np
func getCartesianCoordinates(polar3=Vector3()):
var r = polar3.x
var theta = polar3.y
var phi = polar3.z
var x = r*cos(theta)*sin(phi)
var y = r*cos(phi)
var z = r*sin(theta)*sin(phi)
return Vector3(x,y,z)
func getPolarCoordinates(cartesian3=Vector3()):
var x = cartesian3.x
var y = cartesian3.y
var z = cartesian3.z
var r = sqrt(pow(x,2)+pow(y,2)+pow(z,2))
if r==0:
return Vector3(0,0,0)
var theta = 0
if x!=0:
theta = atan(z/x)+PI if x<0 else 0
else:
theta = (PI/2 * (1 if z>=0 else -1))
var phi = acos(y/r)
return Vector3(r,theta,phi)
I ran some tests and it seemed to work (at least for unit directions):
if do_debug_log:
print(Vector3(1,0,0),"-->",rotateV3AroundPoint(Vector3(1,0,0),Vector3(0,0,0),PI/2))
print(Vector3(-1,0,0),"-->",rotateV3AroundPoint(Vector3(-1,0,0),Vector3(0,0,0),PI/2))
print(Vector3(0,0,1),"-->",rotateV3AroundPoint(Vector3(0,0,1),Vector3(0,0,0),PI/2))
print(Vector3(0,0,-1),"-->",rotateV3AroundPoint(Vector3(0,0,-1),Vector3(0,0,0),PI/2))
print(Vector3(0,1,0),"-->",rotateV3AroundPoint(Vector3(0,1,0),Vector3(0,0,0),PI/2))
print(Vector3(0,-1,0),"-->",rotateV3AroundPoint(Vector3(0,-1,0),Vector3(0,0,0),PI/2))
#(1, 0, 0)-->(-0, -0, -1)
#(-1, 0, 0)-->(-0, -0, 1)
#(0, 0, 1)-->(1, -0, 0)
#(0, 0, -1)-->(-1, -0, 0)
#(0, 1, 0)-->(0, 1, 0)
#(0, -1, 0)-->(-0, -1, -0)
So now to actually rotate, in _event() I use the following code:
if event is InputEventMouseButton:
if event.button_index == key_rotStart:
if !self.movement:
#Rotation initiation
if !self.rotation_initialised:
if do_debug_log:
print("ROT_INIT")
self.rotation_initialMousePos = event.position
self.rotation_initialPos = self.get_translation()
self.rotation_initialRotation = self.rotation
self.rotation_pivot = self.project_ray_normal(event.position)
self.rotation_initialised = true
#This is required to be last, otherwise we initialise twice, which leads to issues.
#Are we rotating?
if event.is_pressed():
self.rotation_mode = true
else:
self.rotation_mode = false
self.rotation_initialised = false
And in _process I use:
if self.rotation_mode:
#Let camera = (x1,z1) and pivot = (x2,z2)
#Using rotation matrix:
# x' = (x2-x1)*cos(theta) - (z2-z1)*sin(theta) + x1
# y' = (x2-x1)*sin(theta) + (z2-z1)*cos(theta) + z1
#It is intended that screen width = 360 degree rotation, and screen hight = 180 degree rotation.
#That is:
# deltaTheta_{yaw} = dx_{mouse} * 2*PI/Width_{screen}
# deltaTheta_{pitch} = dy_{mouse} * 2*PI/Hight_{screen}
#Get change in theta required.
self.rotation_delta = self.rotation_initialMousePos - self.get_viewport().get_mouse_position()
var theta = (rotation_delta.x * (2*PI) / self.get_viewport().size.x)
var phi = (rotation_delta.y * (PI) / self.get_viewport().size.y)
var p = rotateV3AroundPoint(self.rotation_initialPos,self.rotation_pivot,theta,phi)
if do_debug_log:
print(self.rotation_initialPos)
print(self.rotation_pivot)
print(p)
self.translation.x = p.x
self.translation.y = p.y
self.translation.z = p.z
#Point camera towards point:
self.rotation.y = self.rotation_initialRotation.y+theta
self.rotation.x = self.rotation_initialRotation.x-phi
pass
However when testing this code, everything works fine between certain rotations of yaw, but does entirely random stuff with other yaw rotations…
So I really have 2 questions:
- Is there an easier method which does the same thing?
- What am I actually doing wrong here?