mod camera_controller; mod player; pub mod world; use std::time::Instant; use crate::renderer::renderer_view::RendererView; use cgmath::Vector3; use winit::event::Event; use player::Player; use world::World; pub const RENDER_DIST: i32 = 3; pub enum GameState { Paused, Running } pub struct Game { state: GameState, player: Player, #[allow(unused)] world: World, prev_pos: Option>, prev_chunk_pos: Option>, } impl Game { pub fn new(renderer: &mut impl RendererView) -> Self { let mut instance = Self { state: GameState::Paused, player: Player::new(), world: World::init("hello".to_string()), prev_pos: None, prev_chunk_pos: None, }; // Preload chunks around player let timer = Instant::now(); let mut count = 0; for x in -RENDER_DIST..RENDER_DIST+1 { for z in -RENDER_DIST..RENDER_DIST+1 { let pos = Vector3{x, y: 0, z}; instance.world.load_chunk(pos); count += 1; } } println!("Preloaded {} chunks in {} ms (about {} voxels)", count, timer.elapsed().as_millis(), count * world::block::SIZE_QB); // Return generated instance instance } fn load_chunks(&mut self, renderer: &mut impl RendererView, chunk_pos: Vector3) { // Wait for previous chunks to be loaded before loading new ones if !self.world.is_busy() { let prev_chunk_pos = self.prev_chunk_pos.unwrap_or(chunk_pos); let mut dir = chunk_pos - prev_chunk_pos; // Shift max by 1, in one axis if dir.x != 0 { dir = Vector3{x: dir.x.clamp(-1, 1), y:0, z:0 } } else if dir.y != 0 { dir = Vector3{x:0, y:dir.y.clamp(-1, 1), z:0 } } else if dir.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } } else { self.prev_chunk_pos = Some(prev_chunk_pos+dir); return; } // Shift world println!("Shiftin by {:?}", dir); renderer.shift(dir * world::chunk::SIZE as i32); // Load chunks at the edge let center = chunk_pos.clone() + RENDER_DIST*dir; let mask = Vector3{x: dir.x.abs(), y: dir.y.abs(), z: dir.z.abs()}; for a in -RENDER_DIST..RENDER_DIST+1 { let pos = center + Vector3{x: mask.z*a, y: 0, z: mask.x*a}; self.world.load_chunk(pos); } self.prev_chunk_pos = Some(prev_chunk_pos+dir); } } pub fn update(&mut self, delta: f32, renderer: &mut impl RendererView) { // Update camera let camera = renderer.get_camera_transform(); self.player.camera_controller.update(delta, camera); // Get positions let cam_pos = camera.get_position(); let cam_pos = Vector3{x: cam_pos.x as i32, y: cam_pos.y as i32, z: cam_pos.z as i32}; let mut chunk_pos = cam_pos / world::chunk::SIZE as i32; if cam_pos.x < 0 { chunk_pos.x -= 1; } if cam_pos.y < 0 { chunk_pos.y -= 1; } if cam_pos.z < 0 { chunk_pos.z -= 1; } // Update pointing direction let yaw = self.player.camera_controller.get_yaw(); let mut yaw_str = ""; if yaw >= 315.0 || yaw <= 45.0 { yaw_str = "+z"; } else if yaw >= 45.0 && yaw < 135.0 { yaw_str = "+x"; } else if yaw >= 135.0 && yaw < 225.0 { yaw_str = "-z"; } else if yaw >= 225.0 && yaw < 315.0 { yaw_str = "-x"; } renderer.get_ui().set_text("World", 0, format!("Direction: {}", yaw_str)); // Block position if self.prev_pos == None || self.prev_pos != Some(cam_pos) { renderer.get_ui().set_text("World", 1, format!("Position: {}, {}, {}", cam_pos.x, cam_pos.y, cam_pos.z)); self.prev_pos = Some(cam_pos); } // Chunk position if self.prev_chunk_pos == None || self.prev_chunk_pos != Some(chunk_pos) { renderer.get_ui().set_text("World", 2, format!("Chunk: {}, {}, {}", chunk_pos.x, chunk_pos.y, chunk_pos.z)); self.load_chunks(renderer, chunk_pos); } // Receives chunks from workers self.world.update(); // Send dirty chunks to renderer if chunk_pos == self.prev_chunk_pos.unwrap_or(chunk_pos) { for (pos, chunk) in self.world.all_chunks() { if chunk.dirty { renderer.write_chunk(pos, chunk); chunk.dirty = false; } } } } pub fn input(&mut self, event: &Event<()>) { // Update camera controller self.player.camera_controller.handle_input(event); } pub fn update_state(&mut self, requested_state: GameState) -> &GameState { match requested_state { GameState::Paused => { self.player.camera_controller.set_enabled(false); } GameState::Running => { self.player.camera_controller.set_enabled(true); } }; // Currently we are not preventing any state changes, just return requested state self.state = requested_state; &self.state } pub fn get_state(&self) -> &GameState { &self.state } }