Source code for blendersynth.blender.camera

import bpy
import numpy as np
import mathutils
from .utils import handle_vec, SelectObjects, animatable_property
from .bsyn_object import BsynObject
from typing import Union
from .mesh import Mesh
from .curve import Curve
from ..utils import types


def _look_at_rotation(
    obj_camera: bpy.types.Object,
    at: mathutils.Vector = mathutils.Vector((0, 0, 0)),
    up: mathutils.Vector = mathutils.Vector((0, 0, 1)),
) -> mathutils.Euler:
    """Rotate camera to look at 'at', with 'up' maintained.

    :param obj_camera: Camera object
    :param at: Point to look at
    :param up: Up vector"""

    camera_position = obj_camera.location

    z_axis = -(
        at - camera_position
    ).normalized()  # Blender forwards is '-z', so use -ve here
    x_axis = up.cross(z_axis)
    y_axis = z_axis.cross(x_axis).normalized()
    is_close = all(abs(i) < 5e-3 for i in x_axis)

    if is_close:
        replacement = y_axis.cross(z_axis).normalized()
        x_axis = replacement

    R = mathutils.Matrix([x_axis, y_axis, z_axis]).transposed()
    euler = R.to_euler()
    return euler


[docs] class Camera(BsynObject): """Camera object, to handle movement, tracking, etc.""" def __init__(self, camera: bpy.types.Object = None): if camera is None: camera = bpy.context.scene.camera self.camera = camera self._object = camera
[docs] @classmethod def create( cls, name: str = "Camera", location: Union[list, tuple, np.ndarray] = None, rotation: Union[list, tuple, np.ndarray] = None, ) -> "Camera": """Create a new camera object. :param name: Name of camera :param location: Location of camera :param rotation_euler: Rotation of camera in euler angles""" with SelectObjects(): # revert selection after creating camera bpy.ops.object.camera_add(enter_editmode=False, align="VIEW") camera = bpy.context.object camera.name = name cam = cls(camera) if location is not None: cam.set_location(location) if rotation is not None: cam.set_rotation_euler(rotation) return cam
[docs] @classmethod def from_scene(cls, name: str = "Camera") -> "Camera": """Create from a named camera in scene :param name: Name of camera""" assert name in bpy.data.objects, f"Camera {name} does not exist" return cls(bpy.data.objects[name])
[docs] def update(self): """Update view layer and depsgraph""" bpy.context.view_layer.update() bpy.context.evaluated_depsgraph_get()
@property def fov(self): """Field of view in degrees""" return self.camera.data.angle * 180 / np.pi @fov.setter def fov(self, fov): self.set_fov(fov) # FOV is not animatable, so keyframe focal length instead @animatable_property("lens", "data") def set_fov(self, fov): self.camera.data.angle = fov * np.pi / 180 self.update() @property def focal_length(self): """Focal length in mm""" return self.camera.data.lens @focal_length.setter def focal_length(self, focal_length): self.set_focal_length(focal_length) @animatable_property("lens", "data") def set_focal_length(self, focal_length): self.camera.data.lens = focal_length self.update() @property def clip_start(self): return self.camera.data.clip_start @clip_start.setter def clip_start(self, clip_start): self.set_clip_start(clip_start) @animatable_property("clip_start", "data") def set_clip_start(self, clip_start): self.camera.data.clip_start = clip_start self.update() @property def clip_end(self): return self.camera.data.clip_end @clip_end.setter def clip_end(self, clip_end): self.set_clip_end(clip_end) @animatable_property("clip_end", "data") def set_clip_end(self, clip_end): self.camera.data.clip_end = clip_end self.update()
[docs] def look_at( self, at: types.VectorLike = mathutils.Vector((0, 0, 0)), up: types.VectorLike = mathutils.Vector((0, 0, 1)), ): """Look at a point in space, with up vector. :param at: Point to look at :param up: Up vector""" at = handle_vec(at, 3) up = handle_vec(up, 3) self.rotation_euler = _look_at_rotation(self.camera, at, up)
[docs] def look_at_object( self, obj: Union[Mesh, bpy.types.Object], up: types.VectorLike = mathutils.Vector((0, 0, 1)), ): """Look at an object, with up vector. :param obj: Object to look at :param up: Up vector""" if isinstance(obj, Mesh): obj = obj.obj self.look_at(obj.location, up)
def place_and_rotate(self, pos, euler): self.location = pos self.euler = euler def place_and_look_at(self, pos, at, up=mathutils.Vector((0, 0, 1))): pos = handle_vec(pos) at = handle_vec(at) up = handle_vec(up) self.location = pos self.look_at(at, up)