from __future__ import annotations
from contextlib import contextmanager
from typing import TYPE_CHECKING, Callable, Generator
from pyglet.math import Mat4, Vec2, Vec3
from arcade.camera.data_types import (
CameraData,
OrthographicProjectionData,
PerspectiveProjectionData,
)
from arcade.camera.projection_functions import (
generate_orthographic_matrix,
generate_perspective_matrix,
generate_view_matrix,
project_orthographic,
project_perspective,
unproject_orthographic,
unproject_perspective,
)
from arcade.types import Point, Point3
from arcade.window_commands import get_window
if TYPE_CHECKING:
from arcade.application import Window
class _StaticCamera:
def __init__(
self,
view_matrix: Mat4,
projection_matrix: Mat4,
viewport: tuple[int, int, int, int] | None = None,
*,
project_method: (
Callable[[Point, tuple[int, int, int, int], Mat4, Mat4], Vec2] | None
) = None,
unproject_method: (
Callable[[Point, tuple[int, int, int, int], Mat4, Mat4], Vec3] | None
) = None,
window: Window | None = None,
):
self._win: Window = window or get_window()
self._viewport: tuple[int, int, int, int] = viewport or self._win.ctx.viewport
self._view = view_matrix
self._projection = projection_matrix
self._project_method: Callable[[Point, tuple, Mat4, Mat4], Vec2] | None = project_method
self._unproject_method: Callable[[Point, tuple, Mat4, Mat4], Vec3] | None = unproject_method
def use(self):
self._win.current_camera = self
self._win.ctx.viewport = self._viewport
self._win.ctx.projection_matrix = self._projection
self._win.ctx.view_matrix = self._view
@contextmanager
def activate(self) -> Generator[_StaticCamera, None, None]:
prev = self._win.ctx.current_camera
try:
self.use()
yield self
finally:
prev.use()
def project(self, world_coordinate: Point) -> Vec2:
"""
Take a Vec2 or Vec3 of coordinates and return the related screen coordinate
"""
if self._project_method is None:
raise ValueError("This Static Camera was not provided a project method at creation")
pos = self._project_method(world_coordinate, self._viewport, self._view, self._projection)
return pos
def unproject(self, screen_coordinate: Point) -> Vec3:
"""
Take in a pixel coordinate from within
the range of the window size and returns
the world space coordinates.
Essentially reverses the effects of the projector.
Args:
screen_coordinate: A 2D position in pixels from the bottom left of the screen.
This should ALWAYS be in the range of 0.0 - screen size.
Returns:
A 3D vector in world space.
"""
if self._unproject_method is None:
raise ValueError("This Static Camera was not provided an unproject method at creation")
return self._unproject_method(
screen_coordinate, self._viewport, self._view, self._projection
)
[docs]
def static_from_orthographic(
view: CameraData,
orthographic: OrthographicProjectionData,
viewport: tuple[int, int, int, int] | None = None,
*,
window: Window | None = None,
) -> _StaticCamera:
"""
Create a static camera from a CameraData and OrthographicProjectionData
Args:
view: The view matrix to use
orthographic: The orthographic projection to use
viewport: The viewport to use
window: The window to use
"""
return _StaticCamera(
generate_view_matrix(view),
generate_orthographic_matrix(orthographic, view.zoom),
viewport,
window=window,
project_method=project_orthographic,
unproject_method=unproject_orthographic,
)
[docs]
def static_from_perspective(
view: CameraData,
perspective: OrthographicProjectionData,
viewport: tuple[int, int, int, int] | None = None,
*,
window: Window | None = None,
) -> _StaticCamera:
"""
Create a static camera from a CameraData and PerspectiveProjectionData
Args:
view: The view matrix to use
perspective: The perspective projection to use
viewport: The viewport to use
window: The window
"""
return _StaticCamera(
generate_view_matrix(view),
generate_orthographic_matrix(perspective, view.zoom),
viewport,
window=window,
project_method=project_perspective,
unproject_method=unproject_perspective,
)
[docs]
def static_from_raw_orthographic(
projection: tuple[float, float, float, float],
near: float = -100.0,
far: float = 100.0,
zoom: float = 1.0,
position: Point3 = (0.0, 0.0, 0.0),
up: Point3 = (0.0, 1.0, 0.0),
forward: Point3 = (0.0, 0.0, -1.0),
viewport: tuple[int, int, int, int] | None = None,
*,
window: Window | None = None,
) -> _StaticCamera:
"""
Create a static camera from raw orthographic data.
Args:
projection: The orthographic projection to use
near: The near plane
far: The far plane
zoom: The zoom level
position: The position of the camera
up: The up vector
forward: The forward vector
viewport: The viewport
window: The window
"""
view = generate_view_matrix(CameraData(position, up, forward, zoom))
proj = generate_orthographic_matrix(
OrthographicProjectionData(
projection[0], projection[1], projection[2], projection[3], near, far
),
zoom,
)
return _StaticCamera(
view,
proj,
viewport,
window=window,
project_method=project_orthographic,
unproject_method=unproject_orthographic,
)
[docs]
def static_from_raw_perspective(
aspect: float,
fov: float,
near: float = -100.0,
far: float = 100.0,
zoom: float = 1.0,
position: Point3 = (0.0, 0.0, 0.0),
up: Point3 = (0.0, 1.0, 0.0),
forward: Point3 = (0.0, 0.0, -1.0),
viewport: tuple[int, int, int, int] | None = None,
*,
window: Window | None = None,
) -> _StaticCamera:
"""
Create a static camera from raw perspective data.
Args:
aspect: The aspect ratio
fov: The field of view
near: The near plane
far: The far plane
zoom: The zoom level
position: The position of the camera
up: The up vector
forward: The forward vector
viewport: The viewport
window: The window
"""
view = generate_view_matrix(CameraData(position, up, forward, zoom))
proj = generate_perspective_matrix(PerspectiveProjectionData(aspect, fov, near, far), zoom)
return _StaticCamera(
view,
proj,
viewport,
window=window,
project_method=project_perspective,
unproject_method=unproject_perspective,
)
[docs]
def static_from_matrices(
view: Mat4,
projection: Mat4,
viewport: tuple[int, int, int, int] | None,
*,
window: Window | None = None,
project_method: Callable[[Point, tuple[int, int, int, int], Mat4, Mat4], Vec2] | None = None,
unproject_method: Callable[[Point, tuple[int, int, int, int], Mat4, Mat4], Vec3] | None = None,
) -> _StaticCamera:
"""
Create a static camera from raw matrices.
Args:
view: The view matrix
projection: The projection matrix
viewport: The viewport
window: The window
project_method: The project method
unproject_method: The unproject method
"""
return _StaticCamera(
view,
projection,
viewport,
window=window,
project_method=project_method,
unproject_method=unproject_method,
)