mod mipmap;
mod renderer;
mod texture;
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<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::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)),
));
}
}
}