diff --git a/crates/geng-camera/src/camera_2d.rs b/crates/geng-camera/src/camera_2d.rs index d42faa1a..98bf4ce9 100644 --- a/crates/geng-camera/src/camera_2d.rs +++ b/crates/geng-camera/src/camera_2d.rs @@ -1,11 +1,37 @@ use super::*; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum Camera2dFov { + Vertical(f32), + Horizontal(f32), + MinSide(f32), + MaxSide(f32), +} +impl Camera2dFov { + pub fn value_mut(&mut self) -> &mut f32 { + match self { + Camera2dFov::Vertical(value) => value, + Camera2dFov::Horizontal(value) => value, + Camera2dFov::MinSide(value) => value, + Camera2dFov::MaxSide(value) => value, + } + } + pub fn value(&self) -> f32 { + match *self { + Camera2dFov::Vertical(value) => value, + Camera2dFov::Horizontal(value) => value, + Camera2dFov::MinSide(value) => value, + Camera2dFov::MaxSide(value) => value, + } + } +} + /// 2-dimensional camera. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Camera2d { pub center: vec2, pub rotation: Angle, - pub fov: f32, + pub fov: Camera2dFov, } impl AbstractCamera2d for Camera2d { @@ -13,6 +39,14 @@ impl AbstractCamera2d for Camera2d { mat3::rotate(-self.rotation) * mat3::translate(-self.center) } fn projection_matrix(&self, framebuffer_size: vec2) -> mat3 { - mat3::scale(vec2(2.0 * framebuffer_size.y / framebuffer_size.x, 2.0) / self.fov) + let aspect = framebuffer_size.aspect(); + let (vertical, fov) = match self.fov { + Camera2dFov::Vertical(fov) => (true, fov), + Camera2dFov::Horizontal(fov) => (false, fov), + Camera2dFov::MinSide(fov) => (aspect > 1.0, fov), + Camera2dFov::MaxSide(fov) => (aspect < 1.0, fov), + }; + let vertical_fov = if vertical { fov } else { fov / aspect }; + mat3::scale(vec2(2.0 / aspect, 2.0) / vertical_fov) } } diff --git a/examples/03_moving.rs b/examples/03_moving.rs index 6a0bce5d..a78c6975 100644 --- a/examples/03_moving.rs +++ b/examples/03_moving.rs @@ -43,7 +43,7 @@ impl geng::State for State { &geng::Camera2d { center: vec2(0.0, 0.0), rotation: Angle::ZERO, - fov: 15.0, + fov: Camera2dFov::Vertical(15.0), }, "Use arrow keys to move around\nPress Space to reset", vec2::splat(geng::TextAlign::CENTER), diff --git a/examples/04_camera_controls.rs b/examples/04_camera_controls.rs index 39c5608a..b988d5b8 100644 --- a/examples/04_camera_controls.rs +++ b/examples/04_camera_controls.rs @@ -18,7 +18,7 @@ impl State { camera: geng::Camera2d { center: vec2(0.0, 0.0), rotation: Angle::ZERO, - fov: 15.0, + fov: Camera2dFov::Vertical(15.0), }, framebuffer_size: vec2(1.0, 1.0), prev_touch_distance: 0.0, @@ -63,7 +63,8 @@ impl geng::State for State { } // Scrolling to zoom geng::Event::Wheel { delta } => { - self.camera.fov = (self.camera.fov * 1.01f32.powf(-delta as f32)).clamp(1.0, 30.0); + let fov = self.camera.fov.value_mut(); + *fov = (*fov * 1.01f32.powf(-delta as f32)).clamp(1.0, 30.0); } // Drag start geng::Event::MousePress { @@ -126,7 +127,7 @@ impl geng::State for State { } else if self.touches.len() == 2 { let diff = self.touches[0].position - self.touches[1].position; let now_dist = diff.len() as f32; - self.camera.fov /= now_dist / self.prev_touch_distance; + *self.camera.fov.value_mut() /= now_dist / self.prev_touch_distance; self.prev_touch_distance = now_dist; let now_angle = diff.map(|x| x as f32).arg(); let angle_diff = (now_angle - self.prev_touch_angle).normalized_pi(); diff --git a/examples/crabrave/main.rs b/examples/crabrave/main.rs index 93fef4fa..8d8bf55b 100644 --- a/examples/crabrave/main.rs +++ b/examples/crabrave/main.rs @@ -55,7 +55,7 @@ impl geng::State for CrabRave { let camera = geng::Camera2d { center: vec2::ZERO, rotation: Angle::ZERO, - fov: 3.0, + fov: Camera2dFov::MinSide(3.0), }; let x = (self.t / 10.0).cos(); let body = mat3::translate( diff --git a/examples/custom_font_shader.rs b/examples/custom_font_shader.rs index fcca33a6..07b0b794 100644 --- a/examples/custom_font_shader.rs +++ b/examples/custom_font_shader.rs @@ -113,7 +113,7 @@ impl geng::State for State { geng::Camera2d { center: size / 2.0, rotation: Angle::ZERO, - fov: 3.0, + fov: Camera2dFov::Vertical(3.0), } .uniforms(framebuffer.size().map(|x| x as f32)), ), diff --git a/examples/draw/main.rs b/examples/draw/main.rs index cfe44353..c18286c1 100644 --- a/examples/draw/main.rs +++ b/examples/draw/main.rs @@ -84,7 +84,7 @@ impl State { camera: geng::Camera2d { center: vec2::ZERO, rotation: Angle::ZERO, - fov: 10.0, + fov: Camera2dFov::Vertical(10.0), }, objects: vec![], }; diff --git a/examples/line_texture/main.rs b/examples/line_texture/main.rs index ba0f6c09..106dd591 100644 --- a/examples/line_texture/main.rs +++ b/examples/line_texture/main.rs @@ -77,7 +77,7 @@ impl geng::State for Example { &geng::Camera2d { center: vec2(0.0, 2.5), rotation: Angle::ZERO, - fov: 10.0, + fov: Camera2dFov::Vertical(10.0), }, &draw2d::TexturedPolygon::strip( izip![points, ts] diff --git a/examples/pong/game_state.rs b/examples/pong/game_state.rs index 6e28d3d7..a32ee3d3 100644 --- a/examples/pong/game_state.rs +++ b/examples/pong/game_state.rs @@ -43,7 +43,7 @@ impl GameState { camera: geng::Camera2d { center: vec2(0.0, 0.0), rotation: Angle::ZERO, - fov: 400.0, + fov: Camera2dFov::Vertical(400.0), }, boundary: Aabb2::ZERO.extend_symmetric(vec2(ARENA_SIZE_X, ARENA_SIZE_Y) / 2.0), ball: Self::new_ball(), diff --git a/examples/text_input.rs b/examples/text_input.rs index 3f9c1280..de2211bc 100644 --- a/examples/text_input.rs +++ b/examples/text_input.rs @@ -22,7 +22,7 @@ impl geng::State for State { &geng::Camera2d { center: vec2(0.0, 0.0), rotation: Angle::ZERO, - fov: 15.0, + fov: Camera2dFov::Vertical(15.0), }, &self.text, vec2::splat(geng::TextAlign::CENTER), diff --git a/src/lib.rs b/src/lib.rs index a1888799..2ccbd508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ pub mod prelude { pub use crate::{asset::Hot, draw2d, Geng}; - pub use crate::{AbstractCamera2d, AbstractCamera3d, Camera2d}; + pub use crate::{AbstractCamera2d, AbstractCamera3d, Camera2d, Camera2dFov}; pub use ::batbox; pub use ::batbox::prelude::*; // pub use gilrs::{self, Gilrs}; @@ -50,7 +50,7 @@ pub use geng_async_state as async_state; #[cfg(feature = "audio")] pub use geng_audio::{self as audio, *}; pub use geng_camera::{ - self as camera, AbstractCamera2d, AbstractCamera3d, Camera2d, PixelPerfectCamera, + self as camera, AbstractCamera2d, AbstractCamera3d, Camera2d, Camera2dFov, PixelPerfectCamera, }; pub use geng_draw2d::{self as draw2d, Draw2d}; pub use geng_font::{self as font, Font, TextAlign};