mod camera; mod config; mod content; mod input; mod renderer; mod sim; use futures::executor; use glam::Vec2; use rand::random_range; use std::{collections::VecDeque, sync::Arc, time::Instant}; use winit::{ application::ApplicationHandler, event::WindowEvent::{self}, event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, window::Window, }; use crate::{ camera::Camera, config::WINDOW_TITLE, content::{ entities::{entity_cube::entity_cube_def, entity_grenade::entity_grenade_def}, materials::MaterialId, }, input::{ Input::{self}, InputManager, }, renderer::RendererState, sim::{ cell::Cell, lib::force::apply_bullet, rb_manager::DebugRenderMode, sim_manager::{SimCtx, SimManager}, }, }; pub type Error = Box; pub type Result = std::result::Result; struct Config { brush_radius: f32, brush_material: MaterialId, dropper_material: MaterialId, use_threading: bool, cells_render: bool, debug_render: bool, debug_render_mode: DebugRenderMode, } struct Diagnostics { frame_times: VecDeque, fps: f32, } struct App { // core window: Option>, renderer_state: Option, // tests bullet_origin: Option, // game input_manager: InputManager, camera: Option, sim_manager: Option, // renderer last_render: Instant, // state config: Config, diagnostics: Diagnostics, } impl App { // called as often as possible // delta time is the real seconds elapsed since the last time this was called fn update(&mut self, delta_time: f32) { // apply inputs if let Some(camera) = &mut self.camera { camera.handle_camera_input(&self.input_manager, delta_time) } if let Some(sim) = &mut self.sim_manager { // --TEST SPAWNING-- if self.input_manager.pressed(Input::Action1) { sim.create_entity(entity_cube_def( self.input_manager.world_mouse_pos, self.config.dropper_material, )); } if self.input_manager.pressed(Input::Action2) { sim.create_entity(entity_grenade_def(self.input_manager.world_mouse_pos, 3.0)); } if self.input_manager.pressed(Input::Action3) { if let Some(origin) = self.bullet_origin { apply_bullet( &mut SimCtx { input_manager: &self.input_manager, cell_manager: &mut sim.cell_manager, particle_manager: &mut sim.particle_manager, rb_manager: &mut sim.rb_manager, }, origin, self.input_manager.world_mouse_pos, 800, ); self.bullet_origin = None; } else { self.bullet_origin = Some(self.input_manager.world_mouse_pos); } } // --TEST DRAWING-- if self.input_manager.lmb_held || self.input_manager.rmb_held { // start with the bounding box of the drawing brush circle + some margin // clamp the bounding box to the board sie let bb_xl = (self.input_manager.world_mouse_pos.x - self.config.brush_radius) .round() as i32; let bb_xu = (self.input_manager.world_mouse_pos.x + self.config.brush_radius) .round() as i32; let bb_yl = (self.input_manager.world_mouse_pos.y - self.config.brush_radius) .round() as i32; let bb_yu = (self.input_manager.world_mouse_pos.y + self.config.brush_radius) .round() as i32; // for each point, check if the distance is less than the brush size and write the pixel for x in bb_xl..bb_xu { for y in bb_yl..bb_yu { let r = random_range(0.0..1.0); if ((x - self.input_manager.world_mouse_pos.x.round() as i32).pow(2) + (y - self.input_manager.world_mouse_pos.y.round() as i32).pow(2)) < (self.config.brush_radius as i32).pow(2) && r > 0.9 { let cell = if self.input_manager.lmb_held { let mut cell = Cell::from_material(self.config.brush_material); // ensure we simulate on the first tick cell.match_parity(sim.cell_manager.seqno); cell } else { Cell::void() }; sim.cell_manager.set_cell_from_game_position( x, y, cell, false, // wake the chunk ) } } } } sim.update(&self.config, &self.input_manager, delta_time); self.input_manager.reset_for_frame(); } } } impl Default for App { fn default() -> Self { Self { window: None, renderer_state: None, bullet_origin: None, input_manager: InputManager::new(), camera: None, sim_manager: Some(SimManager::new()), last_render: Instant::now(), config: Config { use_threading: true, brush_radius: 10.0, brush_material: MaterialId::Sand, dropper_material: MaterialId::Wood, cells_render: true, debug_render: false, debug_render_mode: DebugRenderMode::default(), }, diagnostics: Diagnostics { fps: 0.0, frame_times: VecDeque::new(), }, } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = Arc::new( event_loop .create_window(Window::default_attributes().with_title(WINDOW_TITLE)) .unwrap(), ); self.window = Some(window.clone()); self.renderer_state = Some(executor::block_on(RendererState::new(window.clone()))); let size = window.inner_size(); self.camera = Some(Camera::new((size.width as i32, size.height as i32))); // let surface = SurfaceTexture::new(size.width, size.height, window_ref); // let mut pixels = Pixels::new(PIXEL_BUFFER_WIDTH, PIXEL_BUFFER_HEIGHT, surface).unwrap(); // pixels.set_scaling_mode(ScalingMode::Fill); } fn window_event( &mut self, event_loop: &ActiveEventLoop, _: winit::window::WindowId, event: WindowEvent, ) { if let Some(renderer_state) = &mut self.renderer_state && let Some(window) = &mut self.window { let egui_response = renderer_state.egui_state.on_window_event(window, &event); // if egui consumed the event, it means we shouldn't treat any e.g., mouse clicks if egui_response.consumed { return; } } if let Some(camera) = &self.camera { self.input_manager.apply_event(&event, &camera); } match event { WindowEvent::Resized(size) => { if let Some(renderer_state) = &mut self.renderer_state && let Some(camera) = &mut self.camera { renderer_state.resize(size.width, size.height); camera.resize((size.width as i32, size.height as i32)); } } WindowEvent::CloseRequested => { event_loop.exit(); } WindowEvent::RedrawRequested => { #[cfg(feature = "profiler")] puffin::GlobalProfiler::lock().new_frame(); puffin::profile_scope!("redraw_requested"); // compute FPS diagnostics let now = Instant::now(); let delta_time = (now - self.last_render).as_secs_f32(); self.last_render = now; self.diagnostics.frame_times.push_back(delta_time); if self.diagnostics.frame_times.len() > 30 { self.diagnostics.frame_times.pop_front(); } let average_frame_time = self.diagnostics.frame_times.iter().sum::() / self.diagnostics.frame_times.len() as f32; self.diagnostics.fps = 1.0 / average_frame_time; self.update(delta_time); if let Some(renderer_state) = &mut self.renderer_state && let Some(sim) = &mut self.sim_manager && let Some(camera) = &mut self.camera { renderer_state.render( sim, camera, &mut self.config, &self.diagnostics, &self.input_manager, ); } } _ => {} } } fn about_to_wait(&mut self, _: &ActiveEventLoop) { if let Some(window) = &self.window { window.request_redraw(); } else { panic!("No window!") } } } #[cfg(feature = "profiler")] fn start_profiler() { let _server = puffin_http::Server::new("127.0.0.1:8585").unwrap(); puffin::set_scopes_on(true); std::mem::forget(_server); // keep serving for the process lifetime std::process::Command::new("puffin_viewer") .args(["--url", "127.0.0.1:8585"]) .spawn() .ok(); // don't die if it isn't installed } fn main() -> Result<()> { #[cfg(feature = "profiler")] start_profiler(); let event_loop = EventLoop::new()?; event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); event_loop.run_app(&mut app)?; Ok(()) }