From 6fa0d46ef30eb123e655ba2c789f075223060b64 Mon Sep 17 00:00:00 2001 From: Adrien Bennadji Date: Mon, 29 Jul 2024 20:43:43 +0200 Subject: [PATCH] winit 0.30 --- Cargo.toml | 8 +- crates/viewer/src/controls.rs | 120 ++++++------- crates/viewer/src/gui.rs | 2 +- crates/viewer/src/main.rs | 308 +++++++++++----------------------- crates/viewer/src/viewer.rs | 230 +++++++++++++++++++++++++ 5 files changed, 392 insertions(+), 276 deletions(-) create mode 100644 crates/viewer/src/viewer.rs diff --git a/Cargo.toml b/Cargo.toml index 879a553..afe55d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,11 +31,11 @@ image = "0.25" ash = "0.38" ash-window = "0.13" raw-window-handle = "0.6" -winit = "0.29" +winit = "0.30" gltf = "1.3" -egui = "0.28" -egui-winit = "0.28" -egui-ash-renderer = { version = "0.5", features = ["dynamic-rendering"] } +egui = "0.29" +egui-winit = "0.29" +egui-ash-renderer = { version = "0.6", features = ["dynamic-rendering"] } [patch.crates-io.gltf] git = "https://github.com/adrien-ben/gltf" diff --git a/crates/viewer/src/controls.rs b/crates/viewer/src/controls.rs index 15cb71b..98323dd 100644 --- a/crates/viewer/src/controls.rs +++ b/crates/viewer/src/controls.rs @@ -1,7 +1,5 @@ use vulkan::winit::{ - event::{ - DeviceEvent, ElementState, Event, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent, - }, + event::{DeviceEvent, ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, }; @@ -20,7 +18,15 @@ pub struct InputState { } impl InputState { - pub fn update(self, event: &Event<()>) -> Self { + pub fn reset(self) -> Self { + Self { + cursor_delta: [0.0, 0.0], + wheel_delta: 0.0, + ..self + } + } + + pub fn handle_window_event(self, event: &WindowEvent) -> Self { let mut is_forward_pressed = None; let mut is_backward_pressed = None; let mut is_left_pressed = None; @@ -30,63 +36,43 @@ impl InputState { let mut is_left_clicked = None; let mut is_right_clicked = None; let mut wheel_delta = self.wheel_delta; - let mut cursor_delta = self.cursor_delta; - if let Event::NewEvents(_) = event { - return Self { - cursor_delta: [0.0, 0.0], - wheel_delta: 0.0, - ..self - }; - } - - if let Event::WindowEvent { event, .. } = event { - match event { - WindowEvent::MouseInput { button, state, .. } => { - let clicked = matches!(state, ElementState::Pressed); - match button { - MouseButton::Left => is_left_clicked = Some(clicked), - MouseButton::Right => is_right_clicked = Some(clicked), - _ => {} - }; - } - WindowEvent::MouseWheel { - delta: MouseScrollDelta::LineDelta(_, v_lines), - .. - } => { - wheel_delta += v_lines; - } - WindowEvent::KeyboardInput { - event: - KeyEvent { - physical_key: PhysicalKey::Code(scancode), - state, - .. - }, - .. - } => { - let pressed = matches!(state, ElementState::Pressed); - match scancode { - KeyCode::KeyW => is_forward_pressed = Some(pressed), - KeyCode::KeyS => is_backward_pressed = Some(pressed), - KeyCode::KeyA => is_left_pressed = Some(pressed), - KeyCode::KeyD => is_right_pressed = Some(pressed), - KeyCode::Space => is_up_pressed = Some(pressed), - KeyCode::ControlLeft => is_down_pressed = Some(pressed), - _ => {} - }; - } - _ => {} + match event { + WindowEvent::MouseInput { button, state, .. } => { + let clicked = matches!(state, ElementState::Pressed); + match button { + MouseButton::Left => is_left_clicked = Some(clicked), + MouseButton::Right => is_right_clicked = Some(clicked), + _ => {} + }; } - } - - if let Event::DeviceEvent { - event: DeviceEvent::MouseMotion { delta: (x, y) }, - .. - } = event - { - cursor_delta[0] += *x as f32; - cursor_delta[1] += *y as f32; + WindowEvent::MouseWheel { + delta: MouseScrollDelta::LineDelta(_, v_lines), + .. + } => { + wheel_delta += v_lines; + } + WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: PhysicalKey::Code(scancode), + state, + .. + }, + .. + } => { + let pressed = matches!(state, ElementState::Pressed); + match scancode { + KeyCode::KeyW => is_forward_pressed = Some(pressed), + KeyCode::KeyS => is_backward_pressed = Some(pressed), + KeyCode::KeyA => is_left_pressed = Some(pressed), + KeyCode::KeyD => is_right_pressed = Some(pressed), + KeyCode::Space => is_up_pressed = Some(pressed), + KeyCode::ControlLeft => is_down_pressed = Some(pressed), + _ => {} + }; + } + _ => {} } Self { @@ -98,8 +84,22 @@ impl InputState { is_down_pressed: is_down_pressed.unwrap_or(self.is_down_pressed), is_left_clicked: is_left_clicked.unwrap_or(self.is_left_clicked), is_right_clicked: is_right_clicked.unwrap_or(self.is_right_clicked), - cursor_delta, wheel_delta, + ..self + } + } + + pub fn handle_device_event(self, event: &DeviceEvent) -> Self { + let mut cursor_delta = self.cursor_delta; + + if let DeviceEvent::MouseMotion { delta: (x, y) } = event { + cursor_delta[0] += *x as f32; + cursor_delta[1] += *y as f32; + } + + Self { + cursor_delta, + ..self } } } diff --git a/crates/viewer/src/gui.rs b/crates/viewer/src/gui.rs index 54a407b..3e6e539 100644 --- a/crates/viewer/src/gui.rs +++ b/crates/viewer/src/gui.rs @@ -190,7 +190,7 @@ impl Gui { fn init_egui(window: &WinitWindow) -> (Context, EguiWinit) { let egui = Context::default(); - let egui_winit = EguiWinit::new(egui.clone(), ViewportId::ROOT, &window, None, None); + let egui_winit = EguiWinit::new(egui.clone(), ViewportId::ROOT, &window, None, None, None); (egui, egui_winit) } diff --git a/crates/viewer/src/main.rs b/crates/viewer/src/main.rs index 71954e8..3c01903 100644 --- a/crates/viewer/src/main.rs +++ b/crates/viewer/src/main.rs @@ -5,19 +5,19 @@ mod error; mod gui; mod loader; mod renderer; +mod viewer; -use crate::{camera::*, config::Config, controls::*, gui::Gui, loader::*, renderer::*}; +use crate::{camera::*, config::Config, controls::*, loader::*, renderer::*}; use clap::Parser; -use environment::*; -use model::{Model, PlaybackMode}; -use std::{cell::RefCell, error::Error, path::PathBuf, rc::Rc, sync::Arc, time::Instant}; +use std::{error::Error, path::PathBuf}; +use viewer::Viewer; use vulkan::*; use winit::{ + application::ApplicationHandler, dpi::PhysicalSize, - event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - keyboard::Key, - window::{Fullscreen, WindowBuilder}, + event::{DeviceEvent, DeviceId, StartCause, WindowEvent}, + event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, + window::{Fullscreen, Window, WindowId}, }; const TITLE: &str = "Gltf Viewer"; @@ -26,217 +26,103 @@ fn main() -> Result<(), Box> { env_logger::init(); log::info!("Welcome to gltf-viewer-rs"); - let cli = Cli::parse(); + let event_loop = EventLoop::new().unwrap(); + event_loop.set_control_flow(ControlFlow::Poll); + let mut app = App::new()?; + event_loop.run_app(&mut app)?; - let config = cli - .config - .as_ref() - .map(config::load_config) - .transpose()? - .unwrap_or_default(); - let enable_debug = cli.debug; - let model_path = cli.file; + Ok(()) +} - run(config, enable_debug, model_path); +struct App { + config: Config, + enable_debug: bool, + model_path: Option, + window: Option, + viewer: Option, +} - Ok(()) +impl App { + fn new() -> Result> { + let cli = Cli::parse(); + + let config = cli + .config + .as_ref() + .map(config::load_config) + .transpose()? + .unwrap_or_default(); + let enable_debug = cli.debug; + let model_path = cli.file; + + Ok(Self { + config, + enable_debug, + model_path, + window: None, + viewer: None, + }) + } } -fn run(config: Config, enable_debug: bool, path: Option) { - log::debug!("Initializing application."); +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window = event_loop + .create_window( + Window::default_attributes() + .with_title(TITLE) + .with_inner_size(PhysicalSize::new( + self.config.resolution().width(), + self.config.resolution().height(), + )) + .with_fullscreen( + self.config + .fullscreen() + .then_some(Fullscreen::Borderless(None)), + ), + ) + .expect("Failed to create window"); + + self.viewer = Some(Viewer::new( + self.config.clone(), + &window, + self.enable_debug, + self.model_path.take(), + )); + self.window = Some(window); + } - let event_loop = EventLoop::new().unwrap(); - event_loop.set_control_flow(ControlFlow::Poll); - let window = WindowBuilder::new() - .with_title(TITLE) - .with_inner_size(PhysicalSize::new( - config.resolution().width(), - config.resolution().height(), - )) - .with_fullscreen(config.fullscreen().then_some(Fullscreen::Borderless(None))) - .build(&event_loop) - .unwrap(); - - let context = Arc::new(Context::new(&window, enable_debug)); - - let mut renderer_settings = RendererSettings::new(&context); - - let environment = Environment::new(&context, config.env().path(), config.env().resolution()); - let mut gui = Gui::new(&window, renderer_settings); - let mut enable_ui = true; - let mut renderer = Renderer::create( - Arc::clone(&context), - &config, - renderer_settings, - environment, - ); - - let mut model: Option>> = None; - let loader = Loader::new(Arc::new(context.new_thread())); - if let Some(p) = path { - loader.load(p); + fn new_events(&mut self, _: &ActiveEventLoop, _: StartCause) { + if let Some(viewer) = self.viewer.as_mut() { + viewer.new_frame(); + } } - let mut camera = Camera::default(); - let mut input_state = InputState::default(); - let mut time = Instant::now(); - let mut dirty_swapchain = false; - - // Main loop - log::debug!("Running application."); - event_loop - .run(move |event, elwt| { - input_state = input_state.update(&event); - - match event { - // Start of event processing - Event::NewEvents(_) => {} - // End of event processing - Event::AboutToWait => { - let new_time = Instant::now(); - let delta_s = (new_time - time).as_secs_f32(); - time = new_time; - - // Load new model - if let Some(loaded_model) = loader.get_model() { - gui.set_model_metadata(loaded_model.metadata().clone()); - model.take(); - - context.graphics_queue_wait_idle(); - let loaded_model = Rc::new(RefCell::new(loaded_model)); - renderer.set_model(&loaded_model); - model = Some(loaded_model); - } - - // Update model - if let Some(model) = model.as_ref() { - let mut model = model.borrow_mut(); - - if gui.should_toggle_animation() { - model.toggle_animation(); - } else if gui.should_stop_animation() { - model.stop_animation(); - } else if gui.should_reset_animation() { - model.reset_animation(); - } else { - let playback_mode = if gui.is_infinite_animation_checked() { - PlaybackMode::Loop - } else { - PlaybackMode::Once - }; - - model.set_animation_playback_mode(playback_mode); - model.set_current_animation(gui.get_selected_animation()); - } - gui.set_animation_playback_state(model.get_animation_playback_state()); - - let delta_s = delta_s * gui.get_animation_speed(); - model.update(delta_s); - } - - // Update camera - { - if gui.should_reset_camera() { - camera = Default::default(); - } - - camera = match gui.camera_mode() { - gui::CameraMode::Orbital => camera.to_orbital(), - gui::CameraMode::Fps => camera.to_fps(), - }; - - camera.fov = gui.camera_fov(); - camera.z_near = gui.camera_z_near(); - camera.z_far = gui.camera_z_far(); - camera.set_move_speed(gui.camera_move_speed()); - - if !gui.is_hovered() { - camera.update(&input_state, delta_s); - gui.set_camera(Some(camera)); - } - } - - // Check if settings changed - if let Some(new_renderer_settings) = gui.get_new_renderer_settings() { - // recreate swapchain if hdr was toggled - if renderer_settings.hdr_enabled != new_renderer_settings.hdr_enabled { - renderer.recreate_swapchain( - window.inner_size().into(), - config.vsync(), - new_renderer_settings.hdr_enabled.unwrap_or_default(), - ); - dirty_swapchain = false; - } - - // Update renderer - renderer.update_settings(new_renderer_settings); - - renderer_settings = new_renderer_settings; - } - - // If swapchain must be recreated wait for windows to not be minimized anymore - if dirty_swapchain { - let PhysicalSize { width, height } = window.inner_size(); - if width > 0 && height > 0 { - renderer.recreate_swapchain( - window.inner_size().into(), - config.vsync(), - renderer_settings.hdr_enabled.unwrap_or_default(), - ); - } else { - return; - } - } - - let gui = enable_ui.then_some(&mut gui); - dirty_swapchain = matches!( - renderer.render(&window, camera, gui), - Err(RenderError::DirtySwapchain) - ); - } - // Window event - Event::WindowEvent { event, .. } => { - gui.handle_event(&window, &event); - match event { - // Dropped file - WindowEvent::DroppedFile(path) => { - log::debug!("File dropped: {:?}", path); - loader.load(path); - } - // Resizing - WindowEvent::Resized(_) => { - dirty_swapchain = true; - } - // Key events - WindowEvent::KeyboardInput { - event: - KeyEvent { - logical_key: Key::Character(c), - state: ElementState::Pressed, - .. - }, - .. - } => { - if c == "h" { - enable_ui = !enable_ui; - } - } - // Exit - WindowEvent::CloseRequested => { - elwt.exit(); - } - _ => (), - } - } - // Cleanup - Event::LoopExiting => { - log::info!("Stopping application"); - renderer.wait_idle_gpu(); - } - _ => (), - } - }) - .unwrap(); + fn about_to_wait(&mut self, _: &ActiveEventLoop) { + self.viewer + .as_mut() + .unwrap() + .end_frame(self.window.as_ref().unwrap()); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) { + if let WindowEvent::CloseRequested = event { + event_loop.exit(); + } + + self.viewer + .as_mut() + .unwrap() + .handle_window_event(self.window.as_ref().unwrap(), &event); + } + + fn device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, event: DeviceEvent) { + self.viewer.as_mut().unwrap().handle_device_event(&event); + } + + fn exiting(&mut self, _: &ActiveEventLoop) { + self.viewer.as_mut().unwrap().on_exit(); + } } #[derive(Parser)] diff --git a/crates/viewer/src/viewer.rs b/crates/viewer/src/viewer.rs new file mode 100644 index 0000000..31288f3 --- /dev/null +++ b/crates/viewer/src/viewer.rs @@ -0,0 +1,230 @@ +use environment::Environment; +use model::{Model, PlaybackMode}; +use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc, time::Instant}; +use vulkan::{ + winit::{ + dpi::PhysicalSize, + event::{DeviceEvent, ElementState, KeyEvent, WindowEvent}, + keyboard::Key, + window::Window, + }, + Context, +}; + +use crate::{ + config::Config, + gui::{self, Gui}, + Camera, InputState, Loader, RenderError, Renderer, RendererSettings, +}; + +pub struct Viewer { + config: Config, + context: Arc, + renderer_settings: RendererSettings, + gui: Gui, + enable_ui: bool, + renderer: Renderer, + model: Option>>, + loader: Loader, + camera: Camera, + input_state: InputState, + time: Instant, + dirty_swapchain: bool, +} + +impl Viewer { + pub fn new( + config: Config, + window: &Window, + enable_debug: bool, + model_path: Option, + ) -> Self { + let context = Arc::new(Context::new(window, enable_debug)); + + let renderer_settings = RendererSettings::new(&context); + + let environment = + Environment::new(&context, config.env().path(), config.env().resolution()); + let gui = Gui::new(window, renderer_settings); + let enable_ui = true; + let renderer = Renderer::create( + Arc::clone(&context), + &config, + renderer_settings, + environment, + ); + + let model: Option>> = None; + let loader = Loader::new(Arc::new(context.new_thread())); + if let Some(p) = model_path { + loader.load(p); + } + + let camera = Camera::default(); + let input_state = InputState::default(); + let time = Instant::now(); + let dirty_swapchain = false; + + Self { + config, + context, + renderer_settings, + gui, + enable_ui, + renderer, + model, + loader, + camera, + input_state, + time, + dirty_swapchain, + } + } + + pub fn new_frame(&mut self) { + self.input_state = self.input_state.reset(); + } + + pub fn handle_window_event(&mut self, window: &Window, event: &WindowEvent) { + self.input_state = self.input_state.handle_window_event(event); + self.gui.handle_event(window, event); + match event { + // Dropped file + WindowEvent::DroppedFile(path) => { + log::debug!("File dropped: {:?}", path); + self.loader.load(path.clone()); + } + // Resizing + WindowEvent::Resized(_) => { + self.dirty_swapchain = true; + } + // Key events + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Character(c), + state: ElementState::Pressed, + .. + }, + .. + } => { + if c == "h" { + self.enable_ui = !self.enable_ui; + } + } + _ => (), + } + } + + pub fn handle_device_event(&mut self, event: &DeviceEvent) { + self.input_state = self.input_state.handle_device_event(event); + } + + pub fn end_frame(&mut self, window: &Window) { + let new_time = Instant::now(); + let delta_s = (new_time - self.time).as_secs_f32(); + self.time = new_time; + + // Load new model + if let Some(loaded_model) = self.loader.get_model() { + self.gui.set_model_metadata(loaded_model.metadata().clone()); + self.model.take(); + + self.context.graphics_queue_wait_idle(); + let loaded_model = Rc::new(RefCell::new(loaded_model)); + self.renderer.set_model(&loaded_model); + self.model = Some(loaded_model); + } + + // Update model + if let Some(model) = self.model.as_ref() { + let mut model = model.borrow_mut(); + + if self.gui.should_toggle_animation() { + model.toggle_animation(); + } else if self.gui.should_stop_animation() { + model.stop_animation(); + } else if self.gui.should_reset_animation() { + model.reset_animation(); + } else { + let playback_mode = if self.gui.is_infinite_animation_checked() { + PlaybackMode::Loop + } else { + PlaybackMode::Once + }; + + model.set_animation_playback_mode(playback_mode); + model.set_current_animation(self.gui.get_selected_animation()); + } + self.gui + .set_animation_playback_state(model.get_animation_playback_state()); + + let delta_s = delta_s * self.gui.get_animation_speed(); + model.update(delta_s); + } + + // Update camera + { + if self.gui.should_reset_camera() { + self.camera = Default::default(); + } + + self.camera = match self.gui.camera_mode() { + gui::CameraMode::Orbital => self.camera.to_orbital(), + gui::CameraMode::Fps => self.camera.to_fps(), + }; + + self.camera.fov = self.gui.camera_fov(); + self.camera.z_near = self.gui.camera_z_near(); + self.camera.z_far = self.gui.camera_z_far(); + self.camera.set_move_speed(self.gui.camera_move_speed()); + + if !self.gui.is_hovered() { + self.camera.update(&self.input_state, delta_s); + self.gui.set_camera(Some(self.camera)); + } + } + + // Check if settings changed + if let Some(new_renderer_settings) = self.gui.get_new_renderer_settings() { + // recreate swapchain if hdr was toggled + if self.renderer_settings.hdr_enabled != new_renderer_settings.hdr_enabled { + self.renderer.recreate_swapchain( + window.inner_size().into(), + self.config.vsync(), + new_renderer_settings.hdr_enabled.unwrap_or_default(), + ); + self.dirty_swapchain = false; + } + + // Update renderer + self.renderer.update_settings(new_renderer_settings); + + self.renderer_settings = new_renderer_settings; + } + + // If swapchain must be recreated wait for windows to not be minimized anymore + if self.dirty_swapchain { + let PhysicalSize { width, height } = window.inner_size(); + if width > 0 && height > 0 { + self.renderer.recreate_swapchain( + window.inner_size().into(), + self.config.vsync(), + self.renderer_settings.hdr_enabled.unwrap_or_default(), + ); + } else { + return; + } + } + + let gui = self.enable_ui.then_some(&mut self.gui); + self.dirty_swapchain = matches!( + self.renderer.render(window, self.camera, gui), + Err(RenderError::DirtySwapchain) + ); + } + + pub fn on_exit(&mut self) { + self.renderer.wait_idle_gpu(); + } +}