mod renderer;
pub mod ui;
mod texture;
mod mipmap;
use std::ops::Add;
use std::process::exit;
use std::sync::Arc;
use std::time::Duration;
use bevy_ecs::schedule::Schedule;
use bevy_ecs::world::World;
use tracing::{debug, error, info};
use web_time::Instant;
use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize;
use winit::event::{MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowId};
use crate::input::MouseWheelEvent;
use crate::rendering::renderer::{Renderer, RenderInitRes};
use crate::rendering::renderer::RenderInitRes::{Initialized, NotReadyYet};
use crate::rendering::ui::UiRenderable;
pub struct App<T: UiRenderable> {
window: Option<Arc<Window>>,
renderer: Option<Renderer<T>>,
#[cfg(target_arch = "wasm32")]
renderer_rx: Option<futures::channel::oneshot::Receiver<RenderInitRes<T>>>,
world: Option<World>,
update_schedule: Option<Schedule>,
ui_renderable: Option<T>
}
impl<T: UiRenderable> App<T> {
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<T: UiRenderable + 'static> ApplicationHandler for App<T> {
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::new(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::new(window.clone(), world, update_schedule, ui_renderable).await;
tx.send(renderer.into()).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) {
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::new(window.clone(), w, u, t).await;
tx.send(renderer.into()).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::new(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))));
}
}
}