use crate::{ Input, config::{CAMERA_MOVEMENT_SPEED, CHUNK_SIZE, PIXEL_BUFFER_HEIGHT, PIXEL_BUFFER_WIDTH}, sim::{chunk::Chunk, world::World}, }; pub struct Camera { // centre coords pub x: f64, pub y: f64, // zoom scale factor, 1.0 = board <-> frame pub zoom: f64, } impl Camera { pub fn handle_camera_input(&mut self, input: &Input, delta_time: f32) { // wasd movement let x: f32 = if input.is_left_pressed { -1.0 } else if input.is_right_pressed { 1.0 } else { 0.0 }; let y: f32 = if input.is_down_pressed { 1.0 } else if input.is_up_pressed { -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.zoom as f32 * CAMERA_MOVEMENT_SPEED * delta_time; let adjusted_y = y / magnitude * self.zoom as f32 * CAMERA_MOVEMENT_SPEED * delta_time; self.x += adjusted_x as f64; self.y += adjusted_y as f64; } pub fn screen_position_to_world(&self, screen_x: f64, screen_y: f64) -> (f64, f64) { let camera_width = self.zoom * f64::from(PIXEL_BUFFER_WIDTH); let camera_height = self.zoom * f64::from(PIXEL_BUFFER_HEIGHT); let camera_start_x = self.x - camera_width / 2.0; let camera_start_y = self.y - camera_height / 2.0; ( (screen_x / PIXEL_BUFFER_WIDTH as f64 * camera_width) + camera_start_x, (screen_y / PIXEL_BUFFER_HEIGHT as f64 * camera_height) + camera_start_y, ) } pub fn write_frame_view(&self, frame: &mut [u8], world: &World) { puffin::profile_function!(); for i in 0..frame.len() / 4 { let idx = i * 4; frame[idx] = 0xFF; frame[idx + 1] = 0xAA; frame[idx + 2] = 0xAA; frame[idx + 3] = 0xFF; } return; const BG: [u8; 4] = [0x00, 0x00, 0x00, 0xFF]; for px in frame.chunks_exact_mut(4) { px.copy_from_slice(&BG); } // world-space rect covered by the screen let (xl, yl) = self.screen_position_to_world(0.0, 0.0); let (xu, yu) = self.screen_position_to_world(PIXEL_BUFFER_WIDTH as f64, PIXEL_BUFFER_HEIGHT as f64); let c = CHUNK_SIZE as i32; let cx0 = (xl.floor() as i32).div_euclid(c); let cx1 = (xu.ceil() as i32).div_euclid(c); let cy0 = (yl.floor() as i32).div_euclid(c); let cy1 = (yu.ceil() as i32).div_euclid(c); let scale = 1.0 / self.zoom; // pixels per cell for cy in cy0..=cy1 { for cx in cx0..=cx1 { let Some(&idx) = world.chunk_position_to_chunk_idx.get(&(cx, cy)) else { continue; }; self.write_chunk(frame, &world.chunks[idx], cx, cy, xl, yl, scale); } } } fn write_chunk( &self, frame: &mut [u8], chunk: &Chunk, cx: i32, cy: i32, xl: f64, yl: f64, scale: f64, ) { let c = CHUNK_SIZE as i32; let w = PIXEL_BUFFER_WIDTH as i32; let h = PIXEL_BUFFER_HEIGHT as i32; for ly in 0..c { let wy = (cy * c + ly) as f64; let sy0 = (((wy - yl) * scale).floor() as i32).max(0); let sy1 = (((wy + 1.0 - yl) * scale).floor() as i32).min(h); if sy0 >= sy1 { continue; } for lx in 0..c { let wx = (cx * c + lx) as f64; let sx0 = (((wx - xl) * scale).floor() as i32).max(0); let sx1 = (((wx + 1.0 - xl) * scale).floor() as i32).min(w); if sx0 >= sx1 { continue; } let color = chunk .get_cell_at_local_position(lx as u8, ly as u8) .material .def() .color; let rgba = [color.0, color.1, color.2, color.3]; for sy in sy0..sy1 { let start = (sy * w + sx0) as usize * 4; let end = (sy * w + sx1) as usize * 4; for px in frame[start..end].chunks_exact_mut(4) { px.copy_from_slice(&rgba); } } } } } }