mod mipmap; mod renderer; mod texture; #[cfg(not(target_arch="wasm32"))] #[path = "assets_native.rs"] pub mod assets; #[cfg(target_arch="wasm32")] #[path = "assets_wasm.rs"] pub mod assets; pub mod ui; use crate::input::MouseWheelEvent; use crate::rendering::renderer::RenderInitRes::{Initialized, NotReadyYet}; #[allow(unused_imports)] use crate::rendering::renderer::{Renderer, RenderInitRes}; use crate::rendering::ui::UiRenderable; use bevy_ecs::schedule::Schedule; use bevy_ecs::world::World; use std::ops::Add; use std::process::exit; use std::sync::Arc; use std::time::Duration; use tracing::info; use web_time::Instant; use winit::application::ApplicationHandler; use winit::dpi::LogicalSize; use winit::event::{MouseScrollDelta, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::window::{Window, WindowId}; pub struct App { window: Option>, renderer: Option>, #[cfg(target_arch = "wasm32")] renderer_rx: Option>>, world: Option, update_schedule: Option, ui_renderable: Option, } impl App { pub fn new(world: World, update_schedule: Schedule, ui_renderable: T) -> Self { Self { window: None, renderer: None, #[cfg(target_arch = "wasm32")] renderer_rx: None, world: Some(world), update_schedule: Some(update_schedule), ui_renderable: Some(ui_renderable), } } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.window.is_none() { let attributes = Window::default_attributes().with_title("StarKingdoms.TK"); let window = Arc::new(event_loop.create_window(attributes).unwrap()); self.window = Some(window.clone()); let world = self.world.take().unwrap(); let update_schedule = self.update_schedule.take().unwrap(); let ui_renderable = self.ui_renderable.take().unwrap(); #[cfg(not(target_arch = "wasm32"))] { let renderer = pollster::block_on(async move { Renderer::try_init(window.clone(), world, update_schedule, ui_renderable).await }); match renderer { Initialized(r) => { self.renderer = Some(r); } NotReadyYet(w, u, t) => { self.world = Some(w); self.update_schedule = Some(u); self.ui_renderable = Some(t); } } } #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; // Add it to the DOM web_sys::window() .unwrap() .document() .unwrap() .body() .unwrap() .append_child(&window.canvas().unwrap()) .unwrap(); let (tx, rx) = futures::channel::oneshot::channel(); self.renderer_rx = Some(rx); wasm_bindgen_futures::spawn_local(async move { let renderer = Renderer::try_init(window.clone(), world, update_schedule, ui_renderable).await; tx.send(renderer).unwrap(); }); } } } fn window_event( &mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent, ) { if event == WindowEvent::CloseRequested { info!("Close requested by underlying event system"); event_loop.exit(); exit(1); } if let Some(renderer) = &mut self.renderer { let egui_response = renderer.gui_winit.on_window_event(&renderer.window, &event); if egui_response.consumed { return; } } match event { WindowEvent::Resized(new) => { if let Some(renderer) = &mut self.renderer { if new.width > 0 && new.height > 0 { renderer.size = new; renderer.scale_factor = renderer.window.scale_factor(); renderer.logical_size = LogicalSize::from_physical(renderer.size, renderer.scale_factor); renderer.surface_configuration.width = new.width; renderer.surface_configuration.height = new.height; renderer .surface .configure(&renderer.device, &renderer.surface_configuration); } } } WindowEvent::ScaleFactorChanged { scale_factor, .. } => { if let Some(renderer) = &mut self.renderer { renderer.scale_factor = scale_factor; renderer.logical_size = LogicalSize::from_physical(renderer.size, renderer.scale_factor); } } WindowEvent::MouseWheel { delta, .. } => { if let Some(renderer) = &mut self.renderer { renderer.world.send_event(match delta { MouseScrollDelta::PixelDelta(pos) => { MouseWheelEvent::Pixel { x: pos.x, y: pos.y } } MouseScrollDelta::LineDelta(x, y) => MouseWheelEvent::Line { x: x as f64, y: y as f64, }, }); } } _ => {} } } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { #[allow(unused_variables)] if let Some(window) = self.window.clone() { #[cfg(target_arch = "wasm32")] { let mut renderer_rxd = false; if let Some(rx) = self.renderer_rx.as_mut() { if let Ok(Some(renderer)) = rx.try_recv() { match renderer { Initialized(r) => { self.renderer = Some(r); renderer_rxd = true; } NotReadyYet(w, u, t) => { let (tx, rx) = futures::channel::oneshot::channel(); self.renderer_rx = Some(rx); wasm_bindgen_futures::spawn_local(async move { let renderer = Renderer::try_init(window.clone(), w, u, t).await; tx.send(renderer).unwrap(); }); } } } } if renderer_rxd { self.renderer_rx = None; } } #[cfg(not(target_arch = "wasm32"))] { if self.renderer.is_none() { if let Some(window) = self.window.clone() { let world = self.world.take().unwrap(); let update_schedule = self.update_schedule.take().unwrap(); let ui_renderable = self.ui_renderable.take().unwrap(); let renderer = pollster::block_on(async move { Renderer::try_init(window.clone(), world, update_schedule, ui_renderable) .await }); match renderer { Initialized(r) => { self.renderer = Some(r); } NotReadyYet(w, u, t) => { self.world = Some(w); self.update_schedule = Some(u); self.ui_renderable = Some(t); } } return; } } } let Some(renderer) = &mut self.renderer else { return; }; renderer.render(); event_loop.set_control_flow(ControlFlow::WaitUntil( Instant::now().add(Duration::from_secs_f64(1.0 / 60.0)), )); } } }