Here is a version of ideasman42's look_at function
that also allows you to roll the camera (or any object) about the axis from camera to target:
def point_at(obj, target, roll=0):
"""
Rotate obj to look at target
:arg obj: the object to be rotated. Usually the camera
:arg target: the location (3-tuple or Vector) to be looked at
:arg roll: The angle of rotation about the axis from obj to target in radians.
Based on: https://blender.stackexchange.com/a/5220/12947 (ideasman42)
"""
if not isinstance(target, mathutils.Vector):
target = mathutils.Vector(target)
loc = obj.location
# direction points from the object to the target
direction = target - loc
tracker, rotator = (('-Z', 'Y'),'Z') if obj.type=='CAMERA' else (('X', 'Z'),'Y') #because new cameras points down(-Z), usually meshes point (-Y)
quat = direction.to_track_quat(*tracker)
# /usr/share/blender/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py
quat = quat.to_matrix().to_4x4()
rollMatrix = mathutils.Matrix.Rotation(roll, 4, rotator)
# remember the current location, since assigning to obj.matrix_world changes it
loc = loc.to_tuple()
#obj.matrix_world = quat * rollMatrix
# in blender 2.8 and above @ is used to multiply matrices
# using * still works but results in unexpected behaviour!
obj.matrix_world = quat @ rollMatrix
obj.location = loc
It can be used like this:
import math
cube = bpy.data.objects["Cube"]
cube.location = (5, -5, 5)
cam = bpy.data.objects["Camera"]
cam.location = (5, -5, -2)
point_at(cam, cube.location, roll=math.radians(45))
