pub mod camera;
pub mod ecs;
pub mod graphics;
pub mod input;
pub mod math;
pub mod prelude;
pub mod time;
pub use anyhow::{Context, Result};
pub use cgmath;
pub use wgpu;
pub use winit;
use winit::keyboard::{KeyCode, PhysicalKey};
pub type StartupSystem = Box<dyn FnOnce(&mut ecs::World, &mut graphics::Renderer)>;
pub type UpdateSystem = Box<dyn Fn(&mut ecs::World, &input::InputState, &time::TimeState)>;
pub struct App {
state: Option<AppState>,
startup_systems: Vec<StartupSystem>,
update_systems: Vec<UpdateSystem>,
title: String,
}
struct AppState {
world: ecs::World,
renderer: graphics::Renderer,
camera_controller: camera::CameraController,
input_state: input::InputState,
time: time::TimeState,
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
impl App {
pub fn new() -> Self {
Self {
state: None,
startup_systems: Vec::new(),
update_systems: Vec::new(),
title: "QSi App".to_string(),
}
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn add_startup_system<F>(mut self, system: F) -> Self
where
F: FnOnce(&mut ecs::World, &mut graphics::Renderer) + 'static,
{
self.startup_systems.push(Box::new(system));
self
}
pub fn add_system<F>(mut self, system: F) -> Self
where
F: Fn(&mut ecs::World, &input::InputState, &time::TimeState) + 'static,
{
self.update_systems.push(Box::new(system));
self
}
pub fn insert_resource<T: 'static + Send + Sync>(self, _resource: T) -> Self {
self
}
pub fn run(self) -> Result<()> {
let event_loop = winit::event_loop::EventLoop::new()?;
event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
let mut handler = AppHandler {
app: self,
systems_executed: false,
};
event_loop.run_app(&mut handler)?;
Ok(())
}
pub fn world(&self) -> Option<&ecs::World> {
self.state.as_ref().map(|s| &s.world)
}
pub fn world_mut(&mut self) -> Option<&mut ecs::World> {
self.state.as_mut().map(|s| &mut s.world)
}
}
struct AppHandler {
app: App,
systems_executed: bool,
}
impl winit::application::ApplicationHandler for AppHandler {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let window = std::sync::Arc::new(
event_loop
.create_window(
winit::window::Window::default_attributes().with_title(&self.app.title),
)
.expect("Failed to create window"),
);
let state = pollster::block_on(AppState::new(window)).expect("Failed to create app state");
self.app.state = Some(state);
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
_window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
if let Some(state) = &mut self.app.state {
if !self.systems_executed {
for system in self.app.startup_systems.drain(..) {
system(&mut state.world, &mut state.renderer);
}
self.systems_executed = true;
}
state.handle_event(event_loop, event, &self.app.update_systems);
}
}
}
impl AppState {
async fn new(window: std::sync::Arc<winit::window::Window>) -> Result<Self> {
let mut world = ecs::World::new();
let renderer = graphics::Renderer::new(window.clone()).await?;
let mut camera_controller = camera::CameraController::new();
let input_state = input::InputState::new();
let time = time::TimeState::new();
let camera_entity = world.create_entity();
world.add_component(camera_entity, math::Transform::default());
world.add_component(camera_entity, camera::Camera::default());
camera_controller.set_camera_entity(camera_entity);
Ok(Self {
world,
renderer,
camera_controller,
input_state,
time,
})
}
fn handle_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
event: winit::event::WindowEvent,
update_systems: &[UpdateSystem],
) {
use winit::event::*;
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(size) => {
self.renderer.resize(size.width, size.height);
}
WindowEvent::RedrawRequested => {
self.update(update_systems);
if let Err(e) = self.render() {
match e {
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated => {
let size = self.renderer.window.inner_size();
self.renderer.resize(size.width, size.height);
}
wgpu::SurfaceError::OutOfMemory => event_loop.exit(),
_ => log::error!("Render error: {e}"),
}
}
}
WindowEvent::MouseInput { button, state, .. } => {
self.input_state.mouse_button(button, state);
self.camera_controller.mouse_button(button, state);
self.renderer.request_redraw();
}
WindowEvent::CursorMoved { position, .. } => {
self.input_state
.set_cursor_position(position.x as f32, position.y as f32);
if self
.camera_controller
.mouse_motion(position.x as f32, position.y as f32)
{
self.renderer.request_redraw();
}
}
WindowEvent::MouseWheel { delta, .. } => {
let scroll_delta = match delta {
MouseScrollDelta::LineDelta(_, y) => y,
MouseScrollDelta::PixelDelta(pos) => pos.y as f32 * 0.1,
};
self.input_state.set_scroll_delta(scroll_delta);
if self.camera_controller.mouse_wheel(scroll_delta) {
self.renderer.request_redraw();
}
}
WindowEvent::ModifiersChanged(new_modifiers) => {
self.input_state.set_modifiers(new_modifiers.state());
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: PhysicalKey::Code(code),
state: key_state,
..
},
..
} => {
self.input_state.key_input(code, key_state);
if (code == KeyCode::Escape && key_state == ElementState::Pressed)
|| (code == KeyCode::KeyC
&& key_state == ElementState::Pressed
&& self.input_state.modifiers().control_key())
{
event_loop.exit();
}
self.renderer.request_redraw();
}
_ => {}
}
}
fn update(&mut self, update_systems: &[UpdateSystem]) {
self.time.update();
self.input_state.update();
for system in update_systems {
system(&mut self.world, &self.input_state, &self.time);
}
self.camera_controller
.update_camera_transform(&mut self.world);
self.renderer
.update_view_matrix(self.camera_controller.view_matrix());
}
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
self.renderer.render(&self.world)
}
}