use glam::{IVec2, Vec2}; use crate::{ config::CAMERA_MOVEMENT_SPEED, input::{Input, InputManager}, sim::{entity::EntityId, sim_manager::SimManager}, }; #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct CameraUniform { pub scale: [f32; 2], pub centre: [f32; 2], } pub struct Camera { pub zoom: f32, pub centre: Vec2, screen_size: IVec2, } impl Camera { fn scale(&self) -> Vec2 { let half = self.zoom * self.screen_size.as_vec2() / 2.0; Vec2::new(1.0 / half.x, -1.0 / half.y) } // (lower, upper) pub fn viewport_bounds_world(&self) -> (Vec2, Vec2) { let lower = self.screen_position_to_world(Vec2::ZERO); let upper = self.screen_position_to_world(self.screen_size.as_vec2()); (lower, upper) } pub fn screen_position_to_world(&self, pos: Vec2) -> Vec2 { let scale = self.scale(); let ndc = pos / self.screen_size.as_vec2() * 2.0 - 1.0; let world = Vec2::new( ndc.x / scale.x + self.centre.x, ndc.y / -scale.y + self.centre.y, ); world } pub fn to_uniform(&self) -> CameraUniform { CameraUniform { scale: self.scale().to_array(), centre: self.centre.to_array(), } } pub fn resize(&mut self, screen_size: IVec2) { self.screen_size = screen_size; } pub fn new(screen_size: IVec2) -> Self { Camera { zoom: 0.12, centre: Vec2::ZERO, screen_size, } } } pub enum CameraControllerMode { Free, TrackEntity(EntityId), } pub struct CameraController { camera: Camera, mode: CameraControllerMode, } impl CameraController { pub fn track_entity(&mut self, entity_id: EntityId) { self.mode = CameraControllerMode::TrackEntity(entity_id); } pub fn free_camera(&mut self) { self.mode = CameraControllerMode::Free; } pub fn camera(&self) -> &Camera { &self.camera } pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera } pub fn update(&mut self, input_manager: &InputManager, sim: &SimManager, delta_time: f32) { match self.mode { CameraControllerMode::Free => { let x: f32 = if input_manager.held(Input::CameraLeft) { -1.0 } else if input_manager.held(Input::CameraRight) { 1.0 } else { 0.0 }; let y: f32 = if input_manager.held(Input::CameraDown) { 1.0 } else if input_manager.held(Input::CameraUp) { -1.0 } else { 0.0 }; if x == 0.0 && y == 0.0 { return; } let magnitude = (x.powi(2) + y.powi(2)).sqrt(); let adjusted_x = x / magnitude * self.camera.zoom * CAMERA_MOVEMENT_SPEED * delta_time; let adjusted_y = y / magnitude * self.camera.zoom * CAMERA_MOVEMENT_SPEED * delta_time; self.camera.centre += Vec2::new(adjusted_x, adjusted_y); } CameraControllerMode::TrackEntity(entity_id) => { if let Some(entity) = sim.entities.get(&entity_id) { let (position, _) = entity.data._transform(&sim.rb_manager).unwrap(); self.camera.centre = position; } } } } pub fn new(screen_size: IVec2, mode: CameraControllerMode) -> Self { let camera = Camera::new(screen_size); CameraController { camera, mode } } }