Source code for simvx.graphics.scene.camera

"""Camera — view and projection matrix management."""


from __future__ import annotations

import logging

import numpy as np

log = logging.getLogger(__name__)

__all__ = ["Camera"]


[docs] class Camera: """Manages view and projection matrices.""" def __init__( self, position: tuple[float, float, float] = (0.0, 0.0, 5.0), target: tuple[float, float, float] = (0.0, 0.0, 0.0), up: tuple[float, float, float] = (0.0, 1.0, 0.0), fov: float = 60.0, aspect: float = 16.0 / 9.0, near: float = 0.1, far: float = 1000.0, ) -> None: self.position = np.array(position, dtype=np.float32) self.target = np.array(target, dtype=np.float32) self.up = np.array(up, dtype=np.float32) self.fov = fov self.aspect = aspect self.near = near self.far = far @property def view_matrix(self) -> np.ndarray: """Compute a look-at view matrix (column-major for GLSL).""" f = self.target - self.position f = f / np.linalg.norm(f) s = np.cross(f, self.up) s = s / np.linalg.norm(s) u = np.cross(s, f) m = np.eye(4, dtype=np.float32) m[0, 0], m[1, 0], m[2, 0] = s m[0, 1], m[1, 1], m[2, 1] = u m[0, 2], m[1, 2], m[2, 2] = -f m[3, 0] = -np.dot(s, self.position) m[3, 1] = -np.dot(u, self.position) m[3, 2] = np.dot(f, self.position) return m @property def projection_matrix(self) -> np.ndarray: """Compute a perspective projection matrix (Vulkan clip space, column-major).""" fov_rad = np.radians(self.fov) f = 1.0 / np.tan(fov_rad / 2.0) n, fa = self.near, self.far m = np.zeros((4, 4), dtype=np.float32) m[0, 0] = f / self.aspect m[1, 1] = -f # Vulkan Y-flip m[2, 2] = fa / (n - fa) m[2, 3] = -1.0 m[3, 2] = (n * fa) / (n - fa) return m