diff --git a/src/legion.rs b/src/legion.rs index 504f804..bc8383f 100644 --- a/src/legion.rs +++ b/src/legion.rs @@ -3,7 +3,7 @@ use crate::camera::Camera; use crate::pass::{self, debug, mesh}; use crate::scene; -use crate::viewport::{ViewportInfo, WinitViewport}; +use crate::viewport::{Viewport, ViewportInfo}; use crate::Renderer; use legion::systems::Builder; use legion::world::SubWorld; @@ -11,6 +11,12 @@ use legion::*; use rayon::prelude::*; use std::sync::Arc; +/// Callbacks trait for acquiring renderer input dynamically. +pub trait RenderCallbacks: 'static + Send + Sync { + fn get_viewports(&mut self) -> Vec<(&dyn Viewport, Camera)>; + fn present(&mut self); +} + /// Initializes the Cyborg renderer within a Legion world. /// /// Uses the provided [Builder] to run all rendering code. @@ -19,7 +25,11 @@ use std::sync::Arc; /// - [Renderer] /// - [ViewportInfo] (TODO: dynamic viewport targeting) /// - [mesh::ShaderInfo] -pub fn build_renderer(resources: &mut Resources, builder: &mut Builder) { +pub fn build_renderer( + callbacks: T, + resources: &mut Resources, + builder: &mut Builder, +) { let renderer = resources.get_mut::().unwrap(); let viewport_info = resources.get::().unwrap(); let mesh_shaders = resources.get::().unwrap(); @@ -52,27 +62,27 @@ pub fn build_renderer(resources: &mut Resources, builder: &mut Builder) { resources.insert(mesh_pass); resources.insert(debug_pass); + resources.insert(callbacks); + builder.add_system(draw_transformed_meshes_system()); builder.add_system(draw_debug_system()); builder.add_system(draw_debug_transformed_system()); - builder.add_system(render_system()); - builder.add_system(present_winit_system()); + builder.add_system(render_system::()); + builder.add_system(present_system::()); } #[system] -#[read_component(WinitViewport)] -#[read_component(Camera)] -fn render( - world: &mut SubWorld, +fn render( + #[resource] callbacks: &mut T, #[resource] renderer: &mut Renderer, #[resource] mesh_pass: &mut pass::RenderPassBox, #[resource] debug_pass: &mut pass::RenderPassBox, ) { - let target_query: Vec<_> = <(&WinitViewport, &Camera)>::query().iter(world).collect(); - if target_query.len() > 1 { + let viewports = callbacks.get_viewports(); + if viewports.len() > 1 { eprintln!("Cyborg does not currently support more than one viewport at a time!"); return; - } else if let Some((viewport, camera)) = target_query.get(0) { + } else if let Some((viewport, camera)) = viewports.get(0) { let mut passes: Vec<&mut dyn pass::RenderPassBoxTrait> = Vec::new(); passes.push(mesh_pass); passes.push(debug_pass); @@ -80,6 +90,11 @@ fn render( } } +#[system] +fn present(#[resource] callbacks: &mut T) { + callbacks.present(); +} + #[system(for_each)] fn draw_transformed_meshes( #[resource] mesh_pass: &pass::RenderPassBox, @@ -92,11 +107,6 @@ fn draw_transformed_meshes( }]); } -#[system(for_each)] -fn present_winit(viewport: &mut WinitViewport) { - viewport.present(); -} - #[system(for_each)] #[filter(!component::())] fn draw_debug( diff --git a/src/main.rs b/src/main.rs index 8d31802..41de749 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use cyborg::camera::Camera; +use cyborg::camera::{Camera, Flycam}; use cyborg::pass::{mesh, RenderPassBox}; use cyborg::shader; use cyborg::storage::mesh::*; @@ -7,6 +7,7 @@ use cyborg::Renderer; use legion::*; use rand::prelude::*; use std::sync::Arc; +use winit::dpi::PhysicalSize; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, @@ -58,6 +59,11 @@ fn update_dt(#[resource] dt: &mut DeltaTime) { dt.last_update = std::time::Instant::now(); } +#[system] +fn update_app(#[resource] app: &mut Application) { + app.update(); +} + #[system(for_each)] fn apply_gravity(#[resource] dt: &DeltaTime, velocity: &mut Velocity) { const GRAVITY: f32 = 6.0; @@ -114,6 +120,7 @@ fn update_debug(draw_list: &mut cyborg::scene::DebugDrawList, velocity: &Velocit fn build_update_schedule() -> Schedule { let mut builder = Schedule::builder(); builder.add_system(update_dt_system()); + builder.add_system(update_app_system()); builder.add_system(apply_gravity_system()); builder.add_system(move_particles_system()); builder.add_system(respawn_particles_system()); @@ -199,6 +206,65 @@ fn make_terrain(attributes: &mesh::Attributes) -> MeshBuffer { mesh } +struct Application { + window: winit::window::Window, + viewport: WinitViewport, + flycam: Flycam, + is_grabbed: bool, +} + +impl Application { + pub fn update(&mut self) { + self.flycam.update(); + self.window.request_redraw(); + } + + pub fn on_mouse_motion(&mut self, x: f64, y: f64) { + if self.is_grabbed { + self.flycam.process_mouse(x, y); + } + } + + pub fn on_mouse_event(&mut self, button: MouseButton, state: ElementState) { + if button == MouseButton::Left && state == ElementState::Pressed && !self.is_grabbed { + self.window.set_cursor_grab(true).unwrap(); + self.window.set_cursor_visible(false); + self.is_grabbed = true; + } + } + + pub fn on_key_event(&mut self, key: VirtualKeyCode, state: ElementState) { + if key == VirtualKeyCode::Escape && state == ElementState::Pressed { + if self.is_grabbed { + self.window.set_cursor_grab(false).unwrap(); + self.window.set_cursor_visible(true); + self.is_grabbed = false; + } + } else { + self.flycam.process_keyboard(key, state); + } + } + + pub fn on_resize(&mut self, new_size: PhysicalSize) { + self.viewport.resize(new_size); + self.flycam.resize(new_size.width, new_size.height); + } + + pub fn on_surface_lost(&mut self) { + self.viewport.resize(self.viewport.size); + } +} + +impl cyborg::legion::RenderCallbacks for Application { + fn get_viewports(&mut self) -> Vec<(&dyn Viewport, Camera)> { + vec![(&self.viewport as &dyn Viewport, self.flycam.get_camera())] + } + + fn present(&mut self) { + self.viewport.present(); + } +} + fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); @@ -216,8 +282,7 @@ fn main() { resources.insert(viewport.get_info().to_owned()); - let mut flycam = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); - let mut is_grabbed = false; + let flycam = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); let shader_store = Arc::new(shader::ShaderStore::new(viewport.device.clone())); let shaders_dir = std::env::current_dir().unwrap(); @@ -235,8 +300,15 @@ fn main() { resources.insert(mesh_shaders); + let application = Application { + window, + viewport, + flycam, + is_grabbed: false, + }; + let mut render_schedule = Schedule::builder(); - cyborg::legion::build_renderer(&mut resources, &mut render_schedule); + cyborg::legion::build_renderer(application, &mut resources, &mut render_schedule); let mut render_schedule = render_schedule.build(); let example_vertices = vec![ @@ -317,17 +389,15 @@ fn main() { } } - let viewport_entity = world.push((viewport, flycam.get_camera())); - event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { - let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); - let viewport = viewport_entry.get_component_mut::().unwrap(); - match viewport.acquire() { - Err(wgpu::SurfaceError::Lost) => viewport.resize(viewport.size), + let mut application = resources.get_mut::().unwrap(); + match application.viewport.acquire() { + Err(wgpu::SurfaceError::Lost) => application.on_surface_lost(), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(e) => eprintln!("error: {:?}", e), Ok(_) => { + drop(application); render_schedule.execute(&mut world, &mut resources); } } @@ -335,70 +405,46 @@ fn main() { Event::MainEventsCleared => { update_schedule.execute(&mut world, &mut resources); shader_watcher.watch(); - flycam.update(); - window.request_redraw(); - - let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); - let camera = viewport_entry.get_component_mut::().unwrap(); - let _ = std::mem::replace(camera, flycam.get_camera()); } Event::DeviceEvent { ref event, .. } => match event { DeviceEvent::MouseMotion { delta } => { - if is_grabbed { - flycam.process_mouse(delta.0, delta.1); - } + let mut application = resources.get_mut::().unwrap(); + application.on_mouse_motion(delta.0, delta.1); } _ => {} }, Event::WindowEvent { ref event, window_id, - } if window_id == window.id() => match event { - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(key), - state, + } => { + let mut application = resources.get_mut::().unwrap(); + if window_id == application.window.id() { + match event { + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state, + .. + }, .. - }, - .. - } => { - if *state == ElementState::Pressed && *key == VirtualKeyCode::Escape { - if is_grabbed { - window.set_cursor_grab(false).unwrap(); - window.set_cursor_visible(true); - is_grabbed = false; + } => { + application.on_key_event(*key, *state); } - } else { - flycam.process_keyboard(*key, *state); + WindowEvent::MouseInput { button, state, .. } => { + application.on_mouse_event(*button, *state); + } + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(physical_size) => { + application.on_resize(*physical_size); + } + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + application.on_resize(**new_inner_size); + } + _ => {} } } - WindowEvent::MouseInput { - button: MouseButton::Left, - state: ElementState::Pressed, - .. - } => { - if !is_grabbed { - window.set_cursor_grab(true).unwrap(); - window.set_cursor_visible(false); - is_grabbed = true; - } - } - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(physical_size) => { - let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); - let viewport = viewport_entry.get_component_mut::().unwrap(); - viewport.resize(*physical_size); - flycam.resize(physical_size.width, physical_size.height); - } - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - let mut viewport_entry = world.entry_mut(viewport_entity).unwrap(); - let viewport = viewport_entry.get_component_mut::().unwrap(); - viewport.resize(**new_inner_size); - flycam.resize(new_inner_size.width, new_inner_size.height); - } - _ => {} - }, + } _ => {} }); }