I'm using the blender game engine and python I made a script that makes an empty follow my cursor in 3D space. (I use the keyboard for height for now). Now I wanted to implement a LookAt function for a general object rather than a camera, using python. I want the object to look exactly at the point I'm hovering (the empty position) at the screen. For now I'm using a cube so basically one face of the cube should always face the empty. So, I thought of using matrices or quaternions but the problem is that All I have is a direction vector and I chose the x axis for the local look direction. So either way I need to calculate the euler angles and convert them to axis-rotation angles. (theta*[axis^]).
The resources I have in the Blender Game Engine is: mathutils (provide quarternions, euler based rotations (via axis-angles), matrices) - though it doesn't have any updated documentation which is just annyoingly horrible! I have to print help to get some sort of info! Now I've been able to make the object look at the empty when I rotate only the Z axis. I used a little trick that handles the angle sign for me using simple trigonometry, so sign is handled and I don't need any matrix trickery or quarternions. The problem begins when I try to rotate once again - I want to rotate the Y axis for the up-down look (as known in 3D we need two sorts of rotations to face someone, the third is just for rotating the view upside-down - "rolling the camrea") since this rotation axis is the look direction vector. Here's my script:
import bge
from mathutils import Vector, Matrix
import math
# Basic stuff
cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
c = scene.objects["Cube"]
e = scene.objects["Empty"]
# axises (we're using localOrientation)
x = Vector((1.0,0.0,0.0))
y = Vector((0.0,1.0,0.0))
z = Vector((0.0,0.0,1.0))
vec = Vector(e.worldPosition - c.worldPosition) # direction vector
# Converting direction vector into euler angles
# Using trigonometry we get: tan(psi) = cos(phi2)/cos(phi1)
# Where phi1 is the angle between x axises (euler angle)
# and phi2 is the euler of the y axises.
# psi is the z rotation angle.
# get cos(euler_angle)
phi1 = vec.dot(x)/vec.length # = cos p1
phi2 = vec.dot(y)/vec.length # = cos p2
phi3 = vec.dot(z)/vec.length # = cos p3
# get the rotation/steer angles
zAngle = math.atan(phi2/phi1)
yAngle = math.atan2(phi3,phi1)
xAngle = math.atan(phi2/phi3)
# use only 2 as the third must adapt (also: view concept - x is the looking direction, rotating it would make rolling)
r = c.localOrientation.to_euler()
r.z = zAngle
r.y = -yAngle
#r.x = xAngle
c.localOrientation = r
Seperately each axis works perfectly, but when combined, there are little jump glitches when I get through the global Y axis. Also, it seems that the "local" orientation in blender is just the same as the "worldOrientation" which is also annoying cause I'm not sure anymore in what frame of reference I'm working anymore. If anyone knows, please help !
Edit 1: Appearantely there's a built in logic block that handles this for me and when I press "3D" it tracks AND succeeds on rotating BOTH axises. Though, I still want to know what's the problem with my script! What did the 3D button do that I didn't?
Edit 2: I tried stop making trigo tricks and found out that when I use local orientation I ALWAYS get a gimbal lock in one axis. That's probably what happened behind the scenes. Thanks for anyone interested, if you have any good trick I'd still be glad to hear =]!