mod camera; mod config; mod content; mod input; mod renderer; mod sim; use futures::executor; 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_bullet_emitter::entity_bullet_emitter_def, entity_cube::entity_cube_def, entity_grenade::entity_grenade_def, }, materials::MaterialId, }, input::{ Input::{self}, InputManager, }, renderer::RendererState, sim::{cell::Cell, rb_manager::DebugRenderMode, sim_manager::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, // 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) { sim.create_entity(entity_bullet_emitter_def( self.input_manager.world_mouse_pos, 3.0, )); } 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, 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))); } 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(()) }