From 32df8a1046e4c99d03b92124accb35ebf46db470 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 19 Sep 2023 22:35:12 +0300 Subject: [PATCH] migrate to winit 0.29.1-beta + wasm fixes - use `EventLoop::spawn` as recommended in `EventLoop::run` docs --- Cargo.toml | 6 +- editor-standalone/src/main.rs | 2 +- editor/src/lib.rs | 253 +++++++-------- examples/2d.rs | 3 +- examples/2d_scene.rs | 3 +- examples/3rd_person.rs | 355 +++++++++++---------- examples/async.rs | 5 +- examples/blend_shape.rs | 5 +- examples/button.rs | 3 +- examples/compressed_textures.rs | 3 +- examples/custom_loader.rs | 84 ++--- examples/custom_loop.rs | 154 ++++----- examples/custom_resource.rs | 3 +- examples/grid.rs | 3 +- examples/inspector.rs | 217 ++++++------- examples/instancing.rs | 5 +- examples/lightmap.rs | 444 +++++++++++++------------- examples/lod.rs | 241 +++++++------- examples/material.rs | 3 +- examples/navmesh.rs | 3 +- examples/plugin.rs | 3 +- examples/raycast2d.rs | 3 +- examples/render_to_texture.rs | 3 +- examples/right_anchored_button.rs | 3 +- examples/save_load.rs | 377 +++++++++++----------- examples/scene.rs | 202 ++++++------ examples/shared/mod.rs | 2 +- examples/simple.rs | 3 +- examples/sound.rs | 512 +++++++++++++++--------------- examples/terrain.rs | 3 +- examples/textbox.rs | 3 +- examples/trasparent.rs | 2 +- examples/ui.rs | 272 ++++++++-------- src/engine/executor.rs | 32 +- 34 files changed, 1655 insertions(+), 1560 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e5ee78f0..47c3994d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ strum = "0.25.0" strum_macros = "0.25.0" clap = { version = "4", features = ["derive"] } spade = "2.1.0" -winit = { version = "0.29.0-beta.0", features = ["serde"] } +winit = { version = "0.29.1-beta", features = ["serde"] } half = "2.2.1" fast_image_resize = "2.7.0" @@ -69,11 +69,11 @@ enable_profiler = ["fyrox-core/enable_profiler"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] glutin = "0.30.10" -glutin-winit = "0.4.0-beta.0" +glutin-winit = "0.4.1-beta" raw-window-handle = "0.5.0" [target.'cfg(target_os = "android")'.dependencies] -winit = { version = "0.29.0-beta.0", features = ["android-native-activity"] } +winit = { version = "0.29.1-beta", features = ["android-native-activity"] } [profile.github-ci] inherits = "dev" diff --git a/editor-standalone/src/main.rs b/editor-standalone/src/main.rs index 349bbd739..72b17aaa9 100644 --- a/editor-standalone/src/main.rs +++ b/editor-standalone/src/main.rs @@ -2,7 +2,7 @@ use fyrox::event_loop::EventLoop; use fyroxed_base::Editor; fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let editor = Editor::new(&event_loop, None); editor.run(event_loop) } diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 87bfe5840..3c204212a 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -2420,151 +2420,156 @@ impl Editor { self.focused || !self.settings.general.suspend_unfocused_editor } - pub fn run(mut self, event_loop: EventLoop<()>) -> ! { + pub fn run(mut self, event_loop: EventLoop<()>) { for_each_plugin!(self.plugins => on_start(&mut self)); - event_loop.run(move |event, _, control_flow| match event { - Event::MainEventsCleared => { - if self.is_active() { - update(&mut self, control_flow); - } - - if self.exit { - *control_flow = ControlFlow::Exit; - - // Kill any active child process on exit. - match self.mode { - Mode::Edit => {} - Mode::Build { ref mut process } - | Mode::Play { - ref mut process, .. - } => { - let _ = process.kill(); - } + event_loop + .run(move |event, _, control_flow| match event { + Event::AboutToWait => { + if self.is_active() { + update(&mut self, control_flow); } - } - } - Event::RedrawRequested(_) => { - if self.is_active() { - // Temporarily disable cameras in currently edited scene. This is needed to prevent any - // scene camera to interfere with the editor camera. - let mut camera_state = Vec::new(); - if let Some(editor_scene) = self.scenes.current_editor_scene_ref() { - let scene = &mut self.engine.scenes[editor_scene.scene]; - let has_preview_camera = - scene.graph.is_valid_handle(editor_scene.preview_camera); - for (handle, camera) in scene.graph.pair_iter_mut().filter_map(|(h, n)| { - if has_preview_camera && h != editor_scene.preview_camera - || !has_preview_camera && h != editor_scene.camera_controller.camera - { - n.cast_mut::().map(|c| (h, c)) - } else { - None + + if self.exit { + *control_flow = ControlFlow::Exit; + + // Kill any active child process on exit. + match self.mode { + Mode::Edit => {} + Mode::Build { ref mut process } + | Mode::Play { + ref mut process, .. + } => { + let _ = process.kill(); } - }) { - camera_state.push((handle, camera.is_enabled())); - camera.set_enabled(false); - } - } - - self.engine.render().unwrap(); - - // Revert state of the cameras. - if let Some(scene) = self.scenes.current_editor_scene_ref() { - for (handle, enabled) in camera_state { - self.engine.scenes[scene.scene].graph[handle] - .as_camera_mut() - .set_enabled(enabled); } } } - } - Event::WindowEvent { ref event, .. } => { - match event { - WindowEvent::CloseRequested => { - self.message_sender.send(Message::Exit { force: false }); + Event::RedrawRequested(_) => { + if self.is_active() { + // Temporarily disable cameras in currently edited scene. This is needed to prevent any + // scene camera to interfere with the editor camera. + let mut camera_state = Vec::new(); + if let Some(editor_scene) = self.scenes.current_editor_scene_ref() { + let scene = &mut self.engine.scenes[editor_scene.scene]; + let has_preview_camera = + scene.graph.is_valid_handle(editor_scene.preview_camera); + for (handle, camera) in + scene.graph.pair_iter_mut().filter_map(|(h, n)| { + if has_preview_camera && h != editor_scene.preview_camera + || !has_preview_camera + && h != editor_scene.camera_controller.camera + { + n.cast_mut::().map(|c| (h, c)) + } else { + None + } + }) + { + camera_state.push((handle, camera.is_enabled())); + camera.set_enabled(false); + } + } + + self.engine.render().unwrap(); + + // Revert state of the cameras. + if let Some(scene) = self.scenes.current_editor_scene_ref() { + for (handle, enabled) in camera_state { + self.engine.scenes[scene.scene].graph[handle] + .as_camera_mut() + .set_enabled(enabled); + } + } } - WindowEvent::Resized(size) => { - if let Err(e) = self.engine.set_frame_size((*size).into()) { - fyrox::core::log::Log::writeln( - MessageKind::Error, - format!("Failed to set renderer size! Reason: {:?}", e), + } + Event::WindowEvent { ref event, .. } => { + match event { + WindowEvent::CloseRequested => { + self.message_sender.send(Message::Exit { force: false }); + } + WindowEvent::Resized(size) => { + if let Err(e) = self.engine.set_frame_size((*size).into()) { + fyrox::core::log::Log::writeln( + MessageKind::Error, + format!("Failed to set renderer size! Reason: {:?}", e), + ); + } + + let logical_size = size.to_logical( + self.engine + .graphics_context + .as_initialized_ref() + .window + .scale_factor(), ); - } - - let logical_size = size.to_logical( self.engine - .graphics_context - .as_initialized_ref() - .window - .scale_factor(), - ); - self.engine - .user_interface - .send_message(WidgetMessage::width( - self.root_grid, - MessageDirection::ToWidget, - logical_size.width, - )); - self.engine - .user_interface - .send_message(WidgetMessage::height( - self.root_grid, - MessageDirection::ToWidget, - logical_size.height, - )); + .user_interface + .send_message(WidgetMessage::width( + self.root_grid, + MessageDirection::ToWidget, + logical_size.width, + )); + self.engine + .user_interface + .send_message(WidgetMessage::height( + self.root_grid, + MessageDirection::ToWidget, + logical_size.height, + )); - if size.width > 0 && size.height > 0 { - self.settings.windows.window_size.x = size.width as f32; - self.settings.windows.window_size.y = size.height as f32; - Log::verify(self.settings.save()); + if size.width > 0 && size.height > 0 { + self.settings.windows.window_size.x = size.width as f32; + self.settings.windows.window_size.y = size.height as f32; + Log::verify(self.settings.save()); + } } - } - WindowEvent::Focused(focused) => { - self.focused = *focused; - } - WindowEvent::Moved(new_position) => { - if new_position.x > 0 && new_position.y > 0 { - self.settings.windows.window_position.x = new_position.x as f32; - self.settings.windows.window_position.y = new_position.y as f32; - Log::verify(self.settings.save()); + WindowEvent::Focused(focused) => { + self.focused = *focused; } + WindowEvent::Moved(new_position) => { + if new_position.x > 0 && new_position.y > 0 { + self.settings.windows.window_position.x = new_position.x as f32; + self.settings.windows.window_position.y = new_position.y as f32; + Log::verify(self.settings.save()); + } + } + WindowEvent::ScaleFactorChanged { scale_factor, .. } => { + set_ui_scaling(&self.engine.user_interface, *scale_factor as f32); + } + _ => (), } - WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - set_ui_scaling(&self.engine.user_interface, *scale_factor as f32); + + self.update_loop_state.request_update_in_current_frame(); + + if let Some(os_event) = translate_event(event) { + self.engine.user_interface.process_os_event(&os_event); } - _ => (), } + Event::LoopExiting => { + Log::verify(self.settings.save()); - self.update_loop_state.request_update_in_current_frame(); - - if let Some(os_event) = translate_event(event) { - self.engine.user_interface.process_os_event(&os_event); + for_each_plugin!(self.plugins => on_exit(&mut self)); } - } - Event::LoopDestroyed => { - Log::verify(self.settings.save()); + _ => { + if !self.update_loop_state.is_suspended() && self.is_active() { + if self.is_suspended { + for_each_plugin!(self.plugins => on_resumed(&mut self)); + self.is_suspended = false; + } - for_each_plugin!(self.plugins => on_exit(&mut self)); - } - _ => { - if !self.update_loop_state.is_suspended() && self.is_active() { - if self.is_suspended { - for_each_plugin!(self.plugins => on_resumed(&mut self)); - self.is_suspended = false; + *control_flow = ControlFlow::Poll; + } else { + if !self.is_suspended { + for_each_plugin!(self.plugins => on_suspended(&mut self)); + self.is_suspended = true; + } + + *control_flow = ControlFlow::Wait; } - - *control_flow = ControlFlow::Poll; - } else { - if !self.is_suspended { - for_each_plugin!(self.plugins => on_suspended(&mut self)); - self.is_suspended = true; - } - - *control_flow = ControlFlow::Wait; } - } - }); + }) + .unwrap(); } } diff --git a/examples/2d.rs b/examples/2d.rs index 3cc0bec4c..d6c5718d6 100644 --- a/examples/2d.rs +++ b/examples/2d.rs @@ -4,6 +4,7 @@ //! //! This example shows simple 2D scene with light sources. +use fyrox::event_loop::EventLoop; use fyrox::resource::texture::Texture; use fyrox::{ asset::manager::ResourceManager, @@ -233,7 +234,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - 2D".to_string(), diff --git a/examples/2d_scene.rs b/examples/2d_scene.rs index 5026bca68..e5869bde7 100644 --- a/examples/2d_scene.rs +++ b/examples/2d_scene.rs @@ -4,6 +4,7 @@ //! //! This example shows simple 2D scene with light sources. +use fyrox::event_loop::EventLoop; use fyrox::resource::model::{Model, ModelResourceExtension}; use fyrox::{ asset::manager::ResourceManager, @@ -171,7 +172,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - 2D Scene".to_string(), diff --git a/examples/3rd_person.rs b/examples/3rd_person.rs index 1fe23d2a8..c3df83a1b 100644 --- a/examples/3rd_person.rs +++ b/examples/3rd_person.rs @@ -59,205 +59,214 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This is main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This is main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Check each frame if our scene is created - here we just trying to lock context - // without blocking, it is important for main thread to be functional while other - // thread still loading data. - if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() { - if let Some(load_result) = load_context.scene_data.take() { - // Add scene to engine - engine will take ownership over scene and will return - // you a handle to scene which can be used later on to borrow it and do some - // actions you need. - game.game_scene = Some(GameScene { - scene: game.engine.scenes.add(load_result.scene), - player: load_result.player, - }); + // Check each frame if our scene is created - here we just trying to lock context + // without blocking, it is important for main thread to be functional while other + // thread still loading data. + if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() + { + if let Some(load_result) = load_context.scene_data.take() { + // Add scene to engine - engine will take ownership over scene and will return + // you a handle to scene which can be used later on to borrow it and do some + // actions you need. + game.game_scene = Some(GameScene { + scene: game.engine.scenes.add(load_result.scene), + player: load_result.player, + }); - // Once scene is loaded, we should hide progress bar and text. + // Once scene is loaded, we should hide progress bar and text. + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_bar, + MessageDirection::ToWidget, + false, + )); + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_text, + MessageDirection::ToWidget, + false, + )); + } + + // Report progress in UI. game.engine .user_interface - .send_message(WidgetMessage::visibility( + .send_message(ProgressBarMessage::progress( interface.progress_bar, MessageDirection::ToWidget, - false, - )); - game.engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_text, - MessageDirection::ToWidget, - false, + load_context.progress, )); + game.engine.user_interface.send_message(TextMessage::text( + interface.progress_text, + MessageDirection::ToWidget, + format!( + "Loading scene: {}%\n{}", + load_context.progress * 100.0, + load_context.message + ), + )); } - // Report progress in UI. - game.engine - .user_interface - .send_message(ProgressBarMessage::progress( - interface.progress_bar, - MessageDirection::ToWidget, - load_context.progress, - )); - game.engine.user_interface.send_message(TextMessage::text( - interface.progress_text, - MessageDirection::ToWidget, - format!( - "Loading scene: {}%\n{}", - load_context.progress * 100.0, - load_context.message - ), - )); - } + // Update scene only if it is loaded. + if let Some(game_scene) = game.game_scene.as_mut() { + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut game.engine.scenes[game_scene.scene]; + game_scene.player.update(scene, fixed_timestep); + } - // Update scene only if it is loaded. - if let Some(game_scene) = game.game_scene.as_mut() { - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut game.engine.scenes[game_scene.scene]; - game_scene.player.update(scene, fixed_timestep); - } - - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - let debug_text = format!( - "Example 03 - 3rd Person\n\ + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context + { + let debug_text = format!( + "Example 03 - 3rd Person\n\ [W][S][A][D] - walk, [SPACE] - jump.\n\ Use [1][2][3][4] to select graphics quality.\n\ {}", - ctx.renderer.get_statistics() - ); - game.engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - debug_text, - )); - } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = game.engine.user_interface.poll_message() { - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ - } - - game.engine - .update(fixed_timestep, control_flow, &mut lag, Default::default()); - lag -= fixed_timestep; - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - game.engine - .initialize_graphics_context(window_target) - .unwrap(); - } - Event::Suspended => { - game.engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - game.engine.render().unwrap(); - } - Event::LoopDestroyed => { - println!("{:?}", fyrox::core::profiler::print()); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = game.engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), + ctx.renderer.get_statistics() ); + game.engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + debug_text, + )); } - // Root UI node should be resized too, otherwise progress bar will stay - // in wrong position after resize. - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context - { - let size = size.to_logical(ctx.window.scale_factor()); - game.engine - .user_interface - .send_message(WidgetMessage::width( - interface.root, - MessageDirection::ToWidget, - size.width, - )); - game.engine - .user_interface - .send_message(WidgetMessage::height( - interface.root, - MessageDirection::ToWidget, - size.height, - )); + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = game.engine.user_interface.poll_message() { + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ } + + game.engine.update( + fixed_timestep, + control_flow, + &mut lag, + Default::default(), + ); + lag -= fixed_timestep; } - WindowEvent::KeyboardInput { event: input, .. } => { - // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene.player.handle_key_event(input, fixed_timestep); - } - let settings = match input.physical_key { - KeyCode::Digit1 => Some(QualitySettings::ultra()), - KeyCode::Digit2 => Some(QualitySettings::high()), - KeyCode::Digit3 => Some(QualitySettings::medium()), - KeyCode::Digit4 => Some(QualitySettings::low()), - _ => None, - }; + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { + ctx.window.request_redraw(); + } + } + Event::Resumed => { + game.engine + .initialize_graphics_context(window_target) + .unwrap(); + } + Event::Suspended => { + game.engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + game.engine.render().unwrap(); + } + Event::LoopExiting => { + println!("{:?}", fyrox::core::profiler::print()); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = game.engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } - if let Some(settings) = settings { - if let GraphicsContext::Initialized(ref mut ctx) = + // Root UI node should be resized too, otherwise progress bar will stay + // in wrong position after resize. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.renderer - .set_quality_settings(&fix_shadows_distance(settings)) - .unwrap(); + let size = size.to_logical(ctx.window.scale_factor()); + game.engine + .user_interface + .send_message(WidgetMessage::width( + interface.root, + MessageDirection::ToWidget, + size.width, + )); + game.engine + .user_interface + .send_message(WidgetMessage::height( + interface.root, + MessageDirection::ToWidget, + size.height, + )); } } - } - _ => (), - } + WindowEvent::KeyboardInput { event: input, .. } => { + // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene.player.handle_key_event(input, fixed_timestep); + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - game.engine.user_interface.process_os_event(&os_event); + let settings = match input.physical_key { + KeyCode::Digit1 => Some(QualitySettings::ultra()), + KeyCode::Digit2 => Some(QualitySettings::high()), + KeyCode::Digit3 => Some(QualitySettings::medium()), + KeyCode::Digit4 => Some(QualitySettings::low()), + _ => None, + }; + + if let Some(settings) = settings { + if let GraphicsContext::Initialized(ref mut ctx) = + game.engine.graphics_context + { + ctx.renderer + .set_quality_settings(&fix_shadows_distance(settings)) + .unwrap(); + } + } + } + _ => (), + } + + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + game.engine.user_interface.process_os_event(&os_event); + } } - } - Event::DeviceEvent { event, .. } => { - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene - .player - .handle_device_event(&event, fixed_timestep); + Event::DeviceEvent { event, .. } => { + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene + .player + .handle_device_event(&event, fixed_timestep); + } } + _ => *control_flow = ControlFlow::Poll, } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/async.rs b/examples/async.rs index c91f15820..5ad4385c8 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -7,6 +7,8 @@ pub mod shared; +use fyrox::event_loop::EventLoop; +use fyrox::keyboard::KeyCode; use fyrox::resource::model::{Model, ModelResourceExtension}; use fyrox::{ asset::manager::ResourceManager, @@ -32,7 +34,6 @@ use fyrox::{ window::WindowAttributes, }; use std::sync::{Arc, Mutex}; -use winit::keyboard::KeyCode; use crate::shared::create_camera; @@ -374,7 +375,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Asynchronous Scene Loading".to_string(), diff --git a/examples/blend_shape.rs b/examples/blend_shape.rs index 55da4f0e1..e632f9f3c 100644 --- a/examples/blend_shape.rs +++ b/examples/blend_shape.rs @@ -1,6 +1,8 @@ pub mod shared; use crate::shared::create_camera; +use fyrox::event_loop::EventLoop; +use fyrox::keyboard::KeyCode; use fyrox::resource::model::{Model, ModelResourceExtension}; use fyrox::{ asset::manager::ResourceManager, @@ -34,7 +36,6 @@ use fyrox::{ window::WindowAttributes, }; use std::collections::BTreeSet; -use winit::keyboard::KeyCode; struct GameSceneLoader { scene: Scene, @@ -272,7 +273,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Blend Shapes".to_string(), diff --git a/examples/button.rs b/examples/button.rs index 6db19a215..fbb683803 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -1,3 +1,4 @@ +use fyrox::event_loop::EventLoop; use fyrox::{ core::{algebra::Vector2, pool::Handle, rand::Rng}, engine::{executor::Executor, GraphicsContext, GraphicsContextParams}, @@ -76,7 +77,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Button".to_string(), diff --git a/examples/compressed_textures.rs b/examples/compressed_textures.rs index 051cd7c44..45c1e8d87 100644 --- a/examples/compressed_textures.rs +++ b/examples/compressed_textures.rs @@ -14,6 +14,7 @@ use fyrox::{ utils::into_gui_texture, window::WindowAttributes, }; +use winit::event_loop::EventLoop; struct Game; @@ -82,7 +83,7 @@ impl Plugin for Game { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Compressed Textures".to_string(), diff --git a/examples/custom_loader.rs b/examples/custom_loader.rs index 26f08eaa1..3297dc45b 100644 --- a/examples/custom_loader.rs +++ b/examples/custom_loader.rs @@ -197,7 +197,7 @@ impl GameSceneLoader { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Custom Resource Loader".to_string(), @@ -241,47 +241,49 @@ fn main() { let fixed_timestep = 1.0 / 60.0; let mut lag = 0.0; - event_loop.run(move |event, window_target, control_flow| match event { - Event::MainEventsCleared => { - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - lag -= fixed_timestep; - } - - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - if let Err(e) = engine.set_frame_size(size.into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } + event_loop + .run(move |event, window_target, control_flow| match event { + Event::AboutToWait => { + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + lag -= fixed_timestep; } - _ => (), - } - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + ctx.window.request_redraw(); + } } - } - _ => *control_flow = ControlFlow::Poll, - }); + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + if let Err(e) = engine.set_frame_size(size.into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + } + _ => (), + } + + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } + } + _ => *control_flow = ControlFlow::Poll, + }) + .unwrap(); } diff --git a/examples/custom_loop.rs b/examples/custom_loop.rs index 97c50b1d6..ea4296358 100644 --- a/examples/custom_loop.rs +++ b/examples/custom_loop.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use winit::window::WindowAttributes; fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); // Create window builder first. let graphics_context_params = GraphicsContextParams { @@ -48,85 +48,87 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // ************************ - // Put your game logic here. - // ************************ - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // ************************ + // Put your game logic here. + // ************************ + // ************************ - // It is very important to update the engine every frame! - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + // It is very important to update the engine every frame! + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - lag -= fixed_timestep; - } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = engine.user_interface.poll_message() { - // ************************ - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ - // ************************ - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref mut graphics_context) = - engine.graphics_context - { - graphics_context.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size(size.into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } + lag -= fixed_timestep; } - // Handle rest of events here if necessary. - _ => (), - } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = engine.user_interface.poll_message() { + // ************************ + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ + // ************************ + } + + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref mut graphics_context) = + engine.graphics_context + { + graphics_context.window.request_redraw(); + } } + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size(size.into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + } + // Handle rest of events here if necessary. + _ => (), + } + + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } + } + // Continue polling messages from OS. + _ => *control_flow = ControlFlow::Poll, } - // Continue polling messages from OS. - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/custom_resource.rs b/examples/custom_resource.rs index 59a181256..42fe0bd6b 100644 --- a/examples/custom_resource.rs +++ b/examples/custom_resource.rs @@ -1,3 +1,4 @@ +use fyrox::event_loop::EventLoop; use fyrox::{ asset::{ event::ResourceEventBroadcaster, @@ -146,7 +147,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Custom Resource".to_string(), diff --git a/examples/grid.rs b/examples/grid.rs index a5d63288d..9f7d857e2 100644 --- a/examples/grid.rs +++ b/examples/grid.rs @@ -1,3 +1,4 @@ +use fyrox::event_loop::EventLoop; use fyrox::{ core::pool::Handle, engine::{executor::Executor, GraphicsContextParams}, @@ -57,7 +58,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Grid".to_string(), diff --git a/examples/inspector.rs b/examples/inspector.rs index 3dba8a542..146f3e5c0 100644 --- a/examples/inspector.rs +++ b/examples/inspector.rs @@ -107,7 +107,7 @@ async fn create_scene(resource_manager: ResourceManager) -> GameScene { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - User Interface".to_string(), @@ -164,125 +164,130 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - let fps = ctx.renderer.get_statistics().frames_per_second; - engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - format!("Example 04 - User Interface\nFPS: {}", fps), - )); + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + let fps = ctx.renderer.get_statistics().frames_per_second; + engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + format!("Example 04 - User Interface\nFPS: {}", fps), + )); + } + + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + lag -= fixed_timestep; } - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - lag -= fixed_timestep; - } - - // It is very important to "pump" messages from UI. This our main point where we communicate - // with user interface. As you saw earlier, there is no callbacks on UI elements, instead we - // use messages to get information from UI elements. This provides perfect decoupling of logic - // from UI elements and works well with borrow checker. - let scene = &mut engine.scenes[scene_handle]; - while let Some(ui_message) = engine.user_interface.poll_message() { - if ui_message.destination() == interface.inspector - && ui_message.direction() == MessageDirection::FromWidget - { - if let Some(InspectorMessage::PropertyChanged(args)) = - ui_message.data::() + // It is very important to "pump" messages from UI. This our main point where we communicate + // with user interface. As you saw earlier, there is no callbacks on UI elements, instead we + // use messages to get information from UI elements. This provides perfect decoupling of logic + // from UI elements and works well with borrow checker. + let scene = &mut engine.scenes[scene_handle]; + while let Some(ui_message) = engine.user_interface.poll_message() { + if ui_message.destination() == interface.inspector + && ui_message.direction() == MessageDirection::FromWidget { - if let FieldKind::Object(ref value) = args.value { - match args.name.as_str() { - "local_scale" => { - value.cast_clone::>(&mut |result| { + if let Some(InspectorMessage::PropertyChanged(args)) = + ui_message.data::() + { + if let FieldKind::Object(ref value) = args.value { + match args.name.as_str() { + "local_scale" => { + value.cast_clone::>(&mut |result| { + scene.graph[model_handle] + .local_transform_mut() + .set_scale(result.unwrap()); + }); + } + "visibility" => value.cast_clone::(&mut |result| { scene.graph[model_handle] - .local_transform_mut() - .set_scale(result.unwrap()); - }); + .set_visibility(result.unwrap()); + }), + "local_rotation" => { + value.cast_clone::>( + &mut |result| { + scene.graph[model_handle] + .local_transform_mut() + .set_rotation(result.unwrap()); + }, + ); + } + "name" => value.cast_clone::(&mut |result| { + scene.graph[model_handle].set_name(result.unwrap()) + }), + // TODO: Add rest of properties. + _ => (), } - "visibility" => value.cast_clone::(&mut |result| { - scene.graph[model_handle].set_visibility(result.unwrap()); - }), - "local_rotation" => { - value.cast_clone::>(&mut |result| { - scene.graph[model_handle] - .local_transform_mut() - .set_rotation(result.unwrap()); - }); - } - "name" => value.cast_clone::(&mut |result| { - scene.graph[model_handle].set_name(result.unwrap()) - }), - // TODO: Add rest of properties. - _ => (), } } } } - } - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + ctx.window.request_redraw(); } - WindowEvent::KeyboardInput { event: input, .. } => { - if input.state == ElementState::Pressed - && input.physical_key == KeyCode::Escape - { - *control_flow = ControlFlow::Exit; - } - } - _ => (), } + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + } + WindowEvent::KeyboardInput { event: input, .. } => { + if input.state == ElementState::Pressed + && input.physical_key == KeyCode::Escape + { + *control_flow = ControlFlow::Exit; + } + } + _ => (), + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } } + Event::DeviceEvent { .. } => { + // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) + } + _ => *control_flow = ControlFlow::Poll, } - Event::DeviceEvent { .. } => { - // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) - } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/instancing.rs b/examples/instancing.rs index 8e8a0383f..0e6baa53a 100644 --- a/examples/instancing.rs +++ b/examples/instancing.rs @@ -8,6 +8,8 @@ pub mod shared; use crate::shared::create_camera; +use fyrox::event_loop::EventLoop; +use fyrox::keyboard::KeyCode; use fyrox::resource::model::{Model, ModelResourceExtension}; use fyrox::resource::texture::Texture; use fyrox::{ @@ -45,7 +47,6 @@ use fyrox::{ }, window::WindowAttributes, }; -use winit::keyboard::KeyCode; struct SceneLoader { scene: Scene, @@ -284,7 +285,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Instancing".to_string(), diff --git a/examples/lightmap.rs b/examples/lightmap.rs index 4da67f0f0..aba3527f5 100644 --- a/examples/lightmap.rs +++ b/examples/lightmap.rs @@ -300,7 +300,7 @@ struct InputController { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Lightmap".to_string(), @@ -366,122 +366,33 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Check each frame if our scene is created - here we just trying to lock context - // without blocking, it is important for main thread to be functional while other - // thread still loading data. - if let Ok(mut load_context) = game_scene.try_lock() { - if let Some(game_scene) = load_context.data.take() { - // Add scene to engine - engine will take ownership over scene and will return - // you a handle to scene which can be used later on to borrow it and do some - // actions you need. - scene_handle = engine.scenes.add(game_scene.scene); - model_handle = game_scene.root; + // Check each frame if our scene is created - here we just trying to lock context + // without blocking, it is important for main thread to be functional while other + // thread still loading data. + if let Ok(mut load_context) = game_scene.try_lock() { + if let Some(game_scene) = load_context.data.take() { + // Add scene to engine - engine will take ownership over scene and will return + // you a handle to scene which can be used later on to borrow it and do some + // actions you need. + scene_handle = engine.scenes.add(game_scene.scene); + model_handle = game_scene.root; - // Once scene is loaded, we should hide progress bar and text. - engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_grid, - MessageDirection::ToWidget, - false, - )); - engine.user_interface.send_message(WindowMessage::close( - interface.choice_window, - MessageDirection::ToWidget, - )); - } - - let stage = match load_context.progress_indicator.stage() { - ProgressStage::LightsCaching => "Caching Lights", - ProgressStage::UvGeneration => "Generating UVs", - ProgressStage::GeometryCaching => "Caching Geometry", - ProgressStage::CalculatingLight => "Calculating Light", - }; - - let message = if load_context.generate_lightmap { - format!( - "Please wait until lightmap is fully generated.\n\ - Stage {} of 4: {}\n\ - Elapsed time: {:.2} s", - load_context.progress_indicator.stage() as u32 + 1, - stage, - load_context.start_time.elapsed().as_secs_f32(), - ) - } else { - format!( - "Please wait until existing lightmap is fully loaded.\n\ - Elapsed time: {:.2} s", - load_context.start_time.elapsed().as_secs_f32(), - ) - }; - - // Report progress in UI. - engine - .user_interface - .send_message(ProgressBarMessage::progress( - interface.progress_bar, - MessageDirection::ToWidget, - load_context.progress_indicator.progress_percent() as f32 / 100.0, - )); - engine.user_interface.send_message(TextMessage::text( - interface.progress_text, - MessageDirection::ToWidget, - message, - )); - } - - // Update scene only if it is loaded. - if scene_handle.is_some() { - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut engine.scenes[scene_handle]; - - // Rotate model according to input controller state. - if input_controller.rotate_left { - model_angle -= 5.0f32.to_radians(); - } else if input_controller.rotate_right { - model_angle += 5.0f32.to_radians(); - } - - scene.graph[model_handle] - .local_transform_mut() - .set_rotation(UnitQuaternion::from_axis_angle( - &Vector3::y_axis(), - model_angle, - )); - } - - // While scene is loading, we will update progress bar. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - let debug_text = format!( - "Example 09 - Lightmap\nUse [A][D] keys to rotate model.\n{}", - ctx.renderer.get_statistics() - ); - engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - debug_text, - )); - } - - while let Some(ui_event) = engine.user_interface.poll_message() { - if let Some(ButtonMessage::Click) = ui_event.data::() { - if ui_event.destination() == interface.cancel { - game_scene.lock().unwrap().cancellation_token.cancel(); + // Once scene is loaded, we should hide progress bar and text. engine .user_interface .send_message(WidgetMessage::visibility( @@ -489,124 +400,217 @@ fn main() { MessageDirection::ToWidget, false, )); - engine - .user_interface - .send_message(WindowMessage::open_modal( + engine.user_interface.send_message(WindowMessage::close( + interface.choice_window, + MessageDirection::ToWidget, + )); + } + + let stage = match load_context.progress_indicator.stage() { + ProgressStage::LightsCaching => "Caching Lights", + ProgressStage::UvGeneration => "Generating UVs", + ProgressStage::GeometryCaching => "Caching Geometry", + ProgressStage::CalculatingLight => "Calculating Light", + }; + + let message = if load_context.generate_lightmap { + format!( + "Please wait until lightmap is fully generated.\n\ + Stage {} of 4: {}\n\ + Elapsed time: {:.2} s", + load_context.progress_indicator.stage() as u32 + 1, + stage, + load_context.start_time.elapsed().as_secs_f32(), + ) + } else { + format!( + "Please wait until existing lightmap is fully loaded.\n\ + Elapsed time: {:.2} s", + load_context.start_time.elapsed().as_secs_f32(), + ) + }; + + // Report progress in UI. + engine + .user_interface + .send_message(ProgressBarMessage::progress( + interface.progress_bar, + MessageDirection::ToWidget, + load_context.progress_indicator.progress_percent() as f32 + / 100.0, + )); + engine.user_interface.send_message(TextMessage::text( + interface.progress_text, + MessageDirection::ToWidget, + message, + )); + } + + // Update scene only if it is loaded. + if scene_handle.is_some() { + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut engine.scenes[scene_handle]; + + // Rotate model according to input controller state. + if input_controller.rotate_left { + model_angle -= 5.0f32.to_radians(); + } else if input_controller.rotate_right { + model_angle += 5.0f32.to_radians(); + } + + scene.graph[model_handle] + .local_transform_mut() + .set_rotation(UnitQuaternion::from_axis_angle( + &Vector3::y_axis(), + model_angle, + )); + } + + // While scene is loading, we will update progress bar. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + let debug_text = format!( + "Example 09 - Lightmap\nUse [A][D] keys to rotate model.\n{}", + ctx.renderer.get_statistics() + ); + engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + debug_text, + )); + } + + while let Some(ui_event) = engine.user_interface.poll_message() { + if let Some(ButtonMessage::Click) = ui_event.data::() { + if ui_event.destination() == interface.cancel { + game_scene.lock().unwrap().cancellation_token.cancel(); + engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_grid, + MessageDirection::ToWidget, + false, + )); + engine + .user_interface + .send_message(WindowMessage::open_modal( + interface.choice_window, + MessageDirection::ToWidget, + true, + )); + } else if ui_event.destination() == interface.generate_new { + game_scene = create_scene_async( + engine.serialization_context.clone(), + engine.resource_manager.clone(), + true, + ); + engine.user_interface.send_message(WindowMessage::close( interface.choice_window, MessageDirection::ToWidget, - true, )); - } else if ui_event.destination() == interface.generate_new { - game_scene = create_scene_async( - engine.serialization_context.clone(), - engine.resource_manager.clone(), - true, - ); - engine.user_interface.send_message(WindowMessage::close( - interface.choice_window, - MessageDirection::ToWidget, - )); - engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_grid, + engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_grid, + MessageDirection::ToWidget, + true, + )); + } else if ui_event.destination() == interface.load_existing { + game_scene = create_scene_async( + engine.serialization_context.clone(), + engine.resource_manager.clone(), + false, + ); + engine.user_interface.send_message(WindowMessage::close( + interface.choice_window, MessageDirection::ToWidget, - true, - )); - } else if ui_event.destination() == interface.load_existing { - game_scene = create_scene_async( - engine.serialization_context.clone(), - engine.resource_manager.clone(), - false, - ); - engine.user_interface.send_message(WindowMessage::close( - interface.choice_window, - MessageDirection::ToWidget, - )); - engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_grid, - MessageDirection::ToWidget, - true, )); + engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_grid, + MessageDirection::ToWidget, + true, + )); + } } } + + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + + lag -= fixed_timestep; } - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - - lag -= fixed_timestep; - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } - - // Root UI node should be resized too, otherwise progress bar will stay - // in wrong position after resize. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - let size = size.to_logical(ctx.window.scale_factor()); - engine.user_interface.send_message(WidgetMessage::width( - interface.root, - MessageDirection::ToWidget, - size.width, - )); - engine.user_interface.send_message(WidgetMessage::height( - interface.root, - MessageDirection::ToWidget, - size.height, - )); - } + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + ctx.window.request_redraw(); } - WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key { - KeyCode::KeyA => { - input_controller.rotate_left = input.state == ElementState::Pressed - } - KeyCode::KeyD => { - input_controller.rotate_right = input.state == ElementState::Pressed + } + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + + // Root UI node should be resized too, otherwise progress bar will stay + // in wrong position after resize. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + let size = size.to_logical(ctx.window.scale_factor()); + engine.user_interface.send_message(WidgetMessage::width( + interface.root, + MessageDirection::ToWidget, + size.width, + )); + engine.user_interface.send_message(WidgetMessage::height( + interface.root, + MessageDirection::ToWidget, + size.height, + )); + } } + WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key + { + KeyCode::KeyA => { + input_controller.rotate_left = input.state == ElementState::Pressed + } + KeyCode::KeyD => { + input_controller.rotate_right = input.state == ElementState::Pressed + } + _ => (), + }, _ => (), - }, - _ => (), - } + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } } + Event::DeviceEvent { .. } => { + // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) + } + _ => *control_flow = ControlFlow::Poll, } - Event::DeviceEvent { .. } => { - // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) - } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/lod.rs b/examples/lod.rs index 0e8ba995f..11a7769a5 100644 --- a/examples/lod.rs +++ b/examples/lod.rs @@ -146,7 +146,7 @@ struct InputController { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { @@ -198,136 +198,139 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut engine.scenes[scene_handle]; + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut engine.scenes[scene_handle]; - // Rotate model according to input controller state. - if input_controller.rotate_left { - model_angle -= 5.0f32.to_radians(); - } else if input_controller.rotate_right { - model_angle += 5.0f32.to_radians(); - } + // Rotate model according to input controller state. + if input_controller.rotate_left { + model_angle -= 5.0f32.to_radians(); + } else if input_controller.rotate_right { + model_angle += 5.0f32.to_radians(); + } - if input_controller.forward { - distance = (distance - 0.1f32).max(0.0f32); - } else if input_controller.backward { - distance = (distance + 0.1f32).min(32.0f32) - } + if input_controller.forward { + distance = (distance - 0.1f32).max(0.0f32); + } else if input_controller.backward { + distance = (distance + 0.1f32).min(32.0f32) + } - scene.graph[model_handle] - .local_transform_mut() - .set_rotation(UnitQuaternion::from_axis_angle( - &Vector3::y_axis(), - model_angle, - )); + scene.graph[model_handle] + .local_transform_mut() + .set_rotation(UnitQuaternion::from_axis_angle( + &Vector3::y_axis(), + model_angle, + )); - scene.graph[camera] - .local_transform_mut() - .set_position(Vector3::new(0.0, 1.5, -distance)); + scene.graph[camera] + .local_transform_mut() + .set_position(Vector3::new(0.0, 1.5, -distance)); - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - let fps = ctx.renderer.get_statistics().frames_per_second; - let text = format!( - "Example 08 - Level of Detail\n\ + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + let fps = ctx.renderer.get_statistics().frames_per_second; + let text = format!( + "Example 08 - Level of Detail\n\ Use [A][D] keys to rotate model, \ [W][S] to zoom in/out.\nFPS: {}\n\ Triangles rendered: {}", - fps, - ctx.renderer.get_statistics().geometry.triangles_rendered - ); - engine.user_interface.send_message(TextMessage::text( - debug_text, - MessageDirection::ToWidget, - text, - )); - } - - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - - lag -= fixed_timestep; - } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = engine.user_interface.poll_message() { - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), + fps, + ctx.renderer.get_statistics().geometry.triangles_rendered ); + engine.user_interface.send_message(TextMessage::text( + debug_text, + MessageDirection::ToWidget, + text, + )); } - } - WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key { - KeyCode::KeyA => { - input_controller.rotate_left = input.state == ElementState::Pressed - } - KeyCode::KeyD => { - input_controller.rotate_right = input.state == ElementState::Pressed - } - KeyCode::KeyW => { - input_controller.forward = input.state == ElementState::Pressed - } - KeyCode::KeyS => { - input_controller.backward = input.state == ElementState::Pressed - } - _ => (), - }, - _ => (), - } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + + lag -= fixed_timestep; + } + + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = engine.user_interface.poll_message() { + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ + } + + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + ctx.window.request_redraw(); + } } + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + } + WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key + { + KeyCode::KeyA => { + input_controller.rotate_left = input.state == ElementState::Pressed + } + KeyCode::KeyD => { + input_controller.rotate_right = input.state == ElementState::Pressed + } + KeyCode::KeyW => { + input_controller.forward = input.state == ElementState::Pressed + } + KeyCode::KeyS => { + input_controller.backward = input.state == ElementState::Pressed + } + _ => (), + }, + _ => (), + } + + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } + } + _ => *control_flow = ControlFlow::Poll, } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/material.rs b/examples/material.rs index 4f411e6fb..479c7cde6 100644 --- a/examples/material.rs +++ b/examples/material.rs @@ -40,6 +40,7 @@ use fyrox::{ }, window::WindowAttributes, }; +use winit::event_loop::EventLoop; struct Game { debug_text: Handle, @@ -147,7 +148,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Materials and Shaders".to_string(), diff --git a/examples/navmesh.rs b/examples/navmesh.rs index c62b6500e..8648c2ea6 100644 --- a/examples/navmesh.rs +++ b/examples/navmesh.rs @@ -7,6 +7,7 @@ pub mod shared; use crate::shared::create_camera; +use fyrox::event_loop::EventLoop; use fyrox::scene::node::NodeTrait; use fyrox::{ asset::manager::ResourceManager, @@ -295,7 +296,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example 12 - Navigation Mesh".to_string(), diff --git a/examples/plugin.rs b/examples/plugin.rs index 99483d3cc..04a9985b0 100644 --- a/examples/plugin.rs +++ b/examples/plugin.rs @@ -1,4 +1,5 @@ use fyrox::engine::GraphicsContext; +use fyrox::event_loop::EventLoop; use fyrox::{ core::{ color::{Color, Hsv}, @@ -44,7 +45,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Plugins".to_string(), diff --git a/examples/raycast2d.rs b/examples/raycast2d.rs index a721addc4..02b9097d1 100644 --- a/examples/raycast2d.rs +++ b/examples/raycast2d.rs @@ -25,6 +25,7 @@ use fyrox::{ Scene, }, }; +use winit::event_loop::EventLoop; struct Game { from: Vector2, @@ -175,7 +176,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - 2D Raycasting".to_string(), diff --git a/examples/render_to_texture.rs b/examples/render_to_texture.rs index 67189eee8..91046888c 100644 --- a/examples/render_to_texture.rs +++ b/examples/render_to_texture.rs @@ -1,3 +1,4 @@ +use fyrox::event_loop::EventLoop; use fyrox::resource::texture::TextureResourceExtension; use fyrox::{ core::{algebra::Vector2, futures::executor::block_on, pool::Handle}, @@ -153,7 +154,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Render Target".to_string(), diff --git a/examples/right_anchored_button.rs b/examples/right_anchored_button.rs index 0d51fe8eb..9fafeb2cc 100644 --- a/examples/right_anchored_button.rs +++ b/examples/right_anchored_button.rs @@ -18,6 +18,7 @@ use fyrox::{ scene::Scene, window::WindowAttributes, }; +use winit::event_loop::EventLoop; struct Game { grid: Handle, @@ -117,7 +118,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Right Anchored Button".to_string(), diff --git a/examples/save_load.rs b/examples/save_load.rs index 97272610a..a5cd6fa1b 100644 --- a/examples/save_load.rs +++ b/examples/save_load.rs @@ -161,215 +161,224 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This is main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This is main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Check each frame if our scene is created - here we just trying to lock context - // without blocking, it is important for main thread to be functional while other - // thread still loading data. - if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() { - if let Some(load_result) = load_context.scene_data.take() { - // Add scene to engine - engine will take ownership over scene and will return - // you a handle to scene which can be used later on to borrow it and do some - // actions you need. - game.game_scene = Some(GameScene { - scene: game.engine.scenes.add(load_result.scene), - player: load_result.player, - }); + // Check each frame if our scene is created - here we just trying to lock context + // without blocking, it is important for main thread to be functional while other + // thread still loading data. + if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() + { + if let Some(load_result) = load_context.scene_data.take() { + // Add scene to engine - engine will take ownership over scene and will return + // you a handle to scene which can be used later on to borrow it and do some + // actions you need. + game.game_scene = Some(GameScene { + scene: game.engine.scenes.add(load_result.scene), + player: load_result.player, + }); - // Once scene is loaded, we should hide progress bar and text. + // Once scene is loaded, we should hide progress bar and text. + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_bar, + MessageDirection::ToWidget, + false, + )); + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_text, + MessageDirection::ToWidget, + false, + )); + } + + // Report progress in UI. game.engine .user_interface - .send_message(WidgetMessage::visibility( + .send_message(ProgressBarMessage::progress( interface.progress_bar, MessageDirection::ToWidget, - false, + load_context.progress, )); - game.engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_text, - MessageDirection::ToWidget, - false, - )); - } - - // Report progress in UI. - game.engine - .user_interface - .send_message(ProgressBarMessage::progress( - interface.progress_bar, + game.engine.user_interface.send_message(TextMessage::text( + interface.progress_text, MessageDirection::ToWidget, - load_context.progress, + format!( + "Loading scene: {}%\n{}", + load_context.progress * 100.0, + load_context.message + ), )); - game.engine.user_interface.send_message(TextMessage::text( - interface.progress_text, - MessageDirection::ToWidget, - format!( - "Loading scene: {}%\n{}", - load_context.progress * 100.0, - load_context.message - ), - )); - } - - // Update scene only if it is loaded. - if let Some(game_scene) = game.game_scene.as_mut() { - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut game.engine.scenes[game_scene.scene]; - game_scene.player.update(scene, fixed_timestep); - } - - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - let fps = ctx.renderer.get_statistics().frames_per_second; - let debug_text = format!( - "Example 06 - Save/load\n[W][S][A][D] - walk, \ - [SPACE] - jump.\nFPS: {}\nUse [1][2][3][4] to select \ - graphics quality.\nUse F5 to save game, F9 to load.", - fps - ); - game.engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - debug_text, - )); - } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = game.engine.user_interface.poll_message() { - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ - } - - game.engine - .update(fixed_timestep, control_flow, &mut lag, Default::default()); - - lag -= fixed_timestep; - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - game.engine - .initialize_graphics_context(window_target) - .unwrap(); - } - Event::Suspended => { - game.engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - game.engine.render().unwrap(); - } - Event::LoopDestroyed => { - println!("{:?}", fyrox::core::profiler::print()); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = game.engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); } - // Root UI node should be resized too, otherwise progress bar will stay - // in wrong position after resize. + // Update scene only if it is loaded. + if let Some(game_scene) = game.game_scene.as_mut() { + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut game.engine.scenes[game_scene.scene]; + game_scene.player.update(scene, fixed_timestep); + } + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - let size = size.to_logical(ctx.window.scale_factor()); - game.engine - .user_interface - .send_message(WidgetMessage::width( - interface.root, - MessageDirection::ToWidget, - size.width, - )); - game.engine - .user_interface - .send_message(WidgetMessage::height( - interface.root, - MessageDirection::ToWidget, - size.height, - )); + let fps = ctx.renderer.get_statistics().frames_per_second; + let debug_text = format!( + "Example 06 - Save/load\n[W][S][A][D] - walk, \ + [SPACE] - jump.\nFPS: {}\nUse [1][2][3][4] to select \ + graphics quality.\nUse F5 to save game, F9 to load.", + fps + ); + game.engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + debug_text, + )); } + + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = game.engine.user_interface.poll_message() { + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ + } + + game.engine.update( + fixed_timestep, + control_flow, + &mut lag, + Default::default(), + ); + + lag -= fixed_timestep; } - WindowEvent::KeyboardInput { event: input, .. } => { - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene.player.handle_key_event(input, fixed_timestep); - } - let settings = match input.physical_key { - KeyCode::Digit1 => Some(QualitySettings::ultra()), - KeyCode::Digit2 => Some(QualitySettings::high()), - KeyCode::Digit3 => Some(QualitySettings::medium()), - KeyCode::Digit4 => Some(QualitySettings::low()), - _ => None, - }; + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { + ctx.window.request_redraw(); + } + } + Event::Resumed => { + game.engine + .initialize_graphics_context(window_target) + .unwrap(); + } + Event::Suspended => { + game.engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + game.engine.render().unwrap(); + } + Event::LoopExiting => { + println!("{:?}", fyrox::core::profiler::print()); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = game.engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } - if let Some(settings) = settings { - if let GraphicsContext::Initialized(ref mut ctx) = + // Root UI node should be resized too, otherwise progress bar will stay + // in wrong position after resize. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.renderer - .set_quality_settings(&fix_shadows_distance(settings)) - .unwrap(); + let size = size.to_logical(ctx.window.scale_factor()); + game.engine + .user_interface + .send_message(WidgetMessage::width( + interface.root, + MessageDirection::ToWidget, + size.width, + )); + game.engine + .user_interface + .send_message(WidgetMessage::height( + interface.root, + MessageDirection::ToWidget, + size.height, + )); } } + WindowEvent::KeyboardInput { event: input, .. } => { + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene.player.handle_key_event(input, fixed_timestep); + } - // Prevent saving/loading while example is starting. - if game.game_scene.is_some() { - // Save/load bound to classic F5 and F9 keys. - match input.physical_key { - KeyCode::F5 => save(&mut game), - KeyCode::F9 => block_on(load(&mut game)), - _ => (), + let settings = match input.physical_key { + KeyCode::Digit1 => Some(QualitySettings::ultra()), + KeyCode::Digit2 => Some(QualitySettings::high()), + KeyCode::Digit3 => Some(QualitySettings::medium()), + KeyCode::Digit4 => Some(QualitySettings::low()), + _ => None, }; - } - } - _ => (), - } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - game.engine.user_interface.process_os_event(&os_event); + if let Some(settings) = settings { + if let GraphicsContext::Initialized(ref mut ctx) = + game.engine.graphics_context + { + ctx.renderer + .set_quality_settings(&fix_shadows_distance(settings)) + .unwrap(); + } + } + + // Prevent saving/loading while example is starting. + if game.game_scene.is_some() { + // Save/load bound to classic F5 and F9 keys. + match input.physical_key { + KeyCode::F5 => save(&mut game), + KeyCode::F9 => block_on(load(&mut game)), + _ => (), + }; + } + } + _ => (), + } + + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + game.engine.user_interface.process_os_event(&os_event); + } } - } - Event::DeviceEvent { event, .. } => { - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene - .player - .handle_device_event(&event, fixed_timestep); + Event::DeviceEvent { event, .. } => { + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene + .player + .handle_device_event(&event, fixed_timestep); + } } + _ => *control_flow = ControlFlow::Poll, } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/scene.rs b/examples/scene.rs index d09a3985b..d7ffb985a 100644 --- a/examples/scene.rs +++ b/examples/scene.rs @@ -79,7 +79,7 @@ struct InputController { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { @@ -126,119 +126,123 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut engine.scenes[scene_handle]; + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut engine.scenes[scene_handle]; - // Rotate model according to input controller state. - if input_controller.rotate_left { - model_angle -= 5.0f32.to_radians(); - } else if input_controller.rotate_right { - model_angle += 5.0f32.to_radians(); + // Rotate model according to input controller state. + if input_controller.rotate_left { + model_angle -= 5.0f32.to_radians(); + } else if input_controller.rotate_right { + model_angle += 5.0f32.to_radians(); + } + + scene.graph[pivot].local_transform_mut().set_rotation( + UnitQuaternion::from_axis_angle(&Vector3::y_axis(), model_angle), + ); + + if let GraphicsContext::Initialized(ref graphics_context) = + engine.graphics_context + { + let fps = graphics_context.renderer.get_statistics().frames_per_second; + let text = format!( + "Example 05 - Scene\nUse [A][D] keys to rotate camera.\nFPS: {}", + fps + ); + engine.user_interface.send_message(TextMessage::text( + debug_text, + MessageDirection::ToWidget, + text, + )); + } + + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + + lag -= fixed_timestep; } - scene.graph[pivot].local_transform_mut().set_rotation( - UnitQuaternion::from_axis_angle(&Vector3::y_axis(), model_angle), - ); + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = engine.user_interface.poll_message() { + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ + } + // Rendering must be explicitly requested and handled after RedrawRequested event is received. if let GraphicsContext::Initialized(ref graphics_context) = engine.graphics_context { - let fps = graphics_context.renderer.get_statistics().frames_per_second; - let text = format!( - "Example 05 - Scene\nUse [A][D] keys to rotate camera.\nFPS: {}", - fps - ); - engine.user_interface.send_message(TextMessage::text( - debug_text, - MessageDirection::ToWidget, - text, - )); + graphics_context.window.request_redraw(); } - - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - - lag -= fixed_timestep; } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = engine.user_interface.poll_message() { - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref graphics_context) = engine.graphics_context - { - graphics_context.window.request_redraw(); + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } - } - WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key { - KeyCode::KeyA => { - input_controller.rotate_left = input.state == ElementState::Pressed - } - KeyCode::KeyD => { - input_controller.rotate_right = input.state == ElementState::Pressed + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } } + WindowEvent::KeyboardInput { event: input, .. } => match input.physical_key + { + KeyCode::KeyA => { + input_controller.rotate_left = input.state == ElementState::Pressed + } + KeyCode::KeyD => { + input_controller.rotate_right = input.state == ElementState::Pressed + } + _ => (), + }, _ => (), - }, - _ => (), - } + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } } + Event::DeviceEvent { .. } => { + // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) + } + _ => *control_flow = ControlFlow::Poll, } - Event::DeviceEvent { .. } => { - // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) - } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/shared/mod.rs b/examples/shared/mod.rs index ec686accc..e687e7cbd 100644 --- a/examples/shared/mod.rs +++ b/examples/shared/mod.rs @@ -127,7 +127,7 @@ pub struct Game { impl Game { pub fn new(title: &str) -> (Self, EventLoop<()>) { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { diff --git a/examples/simple.rs b/examples/simple.rs index 69541d8cb..cea406094 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -7,6 +7,7 @@ pub mod shared; use crate::shared::create_camera; +use fyrox::event_loop::EventLoop; use fyrox::resource::model::{Model, ModelResourceExtension}; use fyrox::resource::texture::Texture; use fyrox::{ @@ -241,7 +242,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Simple".to_string(), diff --git a/examples/sound.rs b/examples/sound.rs index f81089e5f..42b7c4258 100644 --- a/examples/sound.rs +++ b/examples/sound.rs @@ -74,291 +74,301 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This is main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This is main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Check each frame if our scene is created - here we just trying to lock context - // without blocking, it is important for main thread to be functional while other - // thread still loading data. - if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() { - if let Some(mut load_result) = load_context.scene_data.take() { - let animation_player = load_result - .scene - .graph - .find(load_result.player.animation_player, &mut |n| { - n.query_component_ref::().is_some() + // Check each frame if our scene is created - here we just trying to lock context + // without blocking, it is important for main thread to be functional while other + // thread still loading data. + if let Ok(mut load_context) = game.load_context.as_ref().unwrap().try_lock() + { + if let Some(mut load_result) = load_context.scene_data.take() { + let animation_player = load_result + .scene + .graph + .find(load_result.player.animation_player, &mut |n| { + n.query_component_ref::().is_some() + }) + .unwrap() + .0; + + // Once scene is fully loaded, add some signals to walking animation. + (**load_result.scene.graph[animation_player] + .query_component_mut::() + .unwrap() + .animations_mut()) + .get_mut(load_result.player.locomotion_machine.walk_animation) + // Add signals to the walk animation timeline, we'll use signals to emit foot step + // sounds. + .add_signal(AnimationSignal { + id: FOOTSTEP_SIGNAL, + time: 0.2, + name: "Footstep".to_string(), + enabled: true, }) - .unwrap() - .0; + .add_signal(AnimationSignal { + id: FOOTSTEP_SIGNAL, + time: 0.95, + name: "Footstep".to_string(), + enabled: true, + }); - // Once scene is fully loaded, add some signals to walking animation. - (**load_result.scene.graph[animation_player] - .query_component_mut::() - .unwrap() - .animations_mut()) - .get_mut(load_result.player.locomotion_machine.walk_animation) - // Add signals to the walk animation timeline, we'll use signals to emit foot step - // sounds. - .add_signal(AnimationSignal { - id: FOOTSTEP_SIGNAL, - time: 0.2, - name: "Footstep".to_string(), - enabled: true, - }) - .add_signal(AnimationSignal { - id: FOOTSTEP_SIGNAL, - time: 0.95, - name: "Footstep".to_string(), - enabled: true, - }); + // Add scene to engine - engine will take ownership over scene and will return + // you a handle to scene which can be used later on to borrow it and do some + // actions you need. + game.game_scene = Some(GameScene { + scene: game.engine.scenes.add(load_result.scene), + player: load_result.player, + }); - // Add scene to engine - engine will take ownership over scene and will return - // you a handle to scene which can be used later on to borrow it and do some - // actions you need. - game.game_scene = Some(GameScene { - scene: game.engine.scenes.add(load_result.scene), - player: load_result.player, - }); + // Once scene is loaded, we should hide progress bar and text. + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_bar, + MessageDirection::ToWidget, + false, + )); + game.engine + .user_interface + .send_message(WidgetMessage::visibility( + interface.progress_text, + MessageDirection::ToWidget, + false, + )); + } - // Once scene is loaded, we should hide progress bar and text. + // Report progress in UI. game.engine .user_interface - .send_message(WidgetMessage::visibility( + .send_message(ProgressBarMessage::progress( interface.progress_bar, MessageDirection::ToWidget, - false, + load_context.progress, )); - game.engine - .user_interface - .send_message(WidgetMessage::visibility( - interface.progress_text, - MessageDirection::ToWidget, - false, - )); - } - - // Report progress in UI. - game.engine - .user_interface - .send_message(ProgressBarMessage::progress( - interface.progress_bar, + game.engine.user_interface.send_message(TextMessage::text( + interface.progress_text, MessageDirection::ToWidget, - load_context.progress, + format!( + "Loading scene: {}%\n{}", + load_context.progress * 100.0, + load_context.message + ), )); - game.engine.user_interface.send_message(TextMessage::text( - interface.progress_text, - MessageDirection::ToWidget, - format!( - "Loading scene: {}%\n{}", - load_context.progress * 100.0, - load_context.message - ), - )); - } - - // Update scene only if it is loaded. - if let Some(game_scene) = game.game_scene.as_mut() { - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut game.engine.scenes[game_scene.scene]; - game_scene.player.update(scene, fixed_timestep); - - let active_state = scene.graph - [game_scene.player.locomotion_machine.machine] - .query_component_ref::() - .unwrap() - .machine() - .layers()[0] - .active_state(); - - let animation_player = scene.graph[game_scene.player.animation_player] - .query_component_mut::() - .unwrap(); - - let mut events = Vec::new(); - while let Some(event) = (**animation_player.animations_mut()) - .get_mut(game_scene.player.locomotion_machine.walk_animation) - .pop_event() - { - events.push(event); } - while let Some(event) = events.pop() { - // We must play sound only if it was foot step signal and player was in walking state. - if event.signal_id != FOOTSTEP_SIGNAL - || active_state != game_scene.player.locomotion_machine.walk_state + // Update scene only if it is loaded. + if let Some(game_scene) = game.game_scene.as_mut() { + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut game.engine.scenes[game_scene.scene]; + game_scene.player.update(scene, fixed_timestep); + + let active_state = scene.graph + [game_scene.player.locomotion_machine.machine] + .query_component_ref::() + .unwrap() + .machine() + .layers()[0] + .active_state(); + + let animation_player = scene.graph[game_scene.player.animation_player] + .query_component_mut::() + .unwrap(); + + let mut events = Vec::new(); + while let Some(event) = (**animation_player.animations_mut()) + .get_mut(game_scene.player.locomotion_machine.walk_animation) + .pop_event() { - continue; + events.push(event); } - // We'll emit sounds on player's feet. - let mut position = - scene.graph[game_scene.player.pivot].global_position(); - position.y -= 0.5; + while let Some(event) = events.pop() { + // We must play sound only if it was foot step signal and player was in walking state. + if event.signal_id != FOOTSTEP_SIGNAL + || active_state + != game_scene.player.locomotion_machine.walk_state + { + continue; + } - let foot_step = footstep_buffers - [fyrox::rand::thread_rng().gen_range(0..footstep_buffers.len())] - .clone(); + // We'll emit sounds on player's feet. + let mut position = + scene.graph[game_scene.player.pivot].global_position(); + position.y -= 0.5; - // Create new temporary foot step sound source. - SoundBuilder::new( - BaseBuilder::new().with_local_transform( - TransformBuilder::new() - .with_local_position(position) - .build(), - ), - ) - // Specify the name of the effect to which the sound will attach to. - .with_audio_bus(AudioBusGraph::PRIMARY_BUS.to_string()) - // Fyrox provides built-in way to create temporary sounds that will die immediately - // after first play. This is very useful for foot step sounds. - .with_play_once(true) - .with_buffer(Some(foot_step)) - // Every sound source must be explicitly set to Playing status, otherwise it will be stopped. - .with_status(Status::Playing) - .build(&mut scene.graph); - } - } + let foot_step = footstep_buffers[fyrox::rand::thread_rng() + .gen_range(0..footstep_buffers.len())] + .clone(); - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - let fps = ctx.renderer.get_statistics().frames_per_second; - let debug_text = format!( - "Example 07 - Sound\n[W][S][A][D] - walk, [SPACE] - jump.\n\ - FPS: {}\nUse [1][2][3][4] to select graphics quality.", - fps - ); - game.engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - debug_text, - )); - } - - // It is very important to "pump" messages from UI. Even if don't need to - // respond to such message, you should call this method, otherwise UI - // might behave very weird. - while let Some(_ui_event) = game.engine.user_interface.poll_message() { - // ************************ - // Put your data model synchronization code here. It should - // take message and update data in your game according to - // changes in UI. - // ************************ - } - - game.engine - .update(fixed_timestep, control_flow, &mut lag, Default::default()); - - lag -= fixed_timestep; - } - - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - game.engine - .initialize_graphics_context(window_target) - .unwrap(); - } - Event::Suspended => { - game.engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - game.engine.render().unwrap(); - } - Event::LoopDestroyed => { - println!("{:?}", fyrox::core::profiler::print()); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = game.engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); + // Create new temporary foot step sound source. + SoundBuilder::new( + BaseBuilder::new().with_local_transform( + TransformBuilder::new() + .with_local_position(position) + .build(), + ), + ) + // Specify the name of the effect to which the sound will attach to. + .with_audio_bus(AudioBusGraph::PRIMARY_BUS.to_string()) + // Fyrox provides built-in way to create temporary sounds that will die immediately + // after first play. This is very useful for foot step sounds. + .with_play_once(true) + .with_buffer(Some(foot_step)) + // Every sound source must be explicitly set to Playing status, otherwise it will be stopped. + .with_status(Status::Playing) + .build(&mut scene.graph); + } } - // Root UI node should be resized too, otherwise progress bar will stay - // in wrong position after resize. if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - let size = size.to_logical(ctx.window.scale_factor()); - game.engine - .user_interface - .send_message(WidgetMessage::width( - interface.root, - MessageDirection::ToWidget, - size.width, - )); - game.engine - .user_interface - .send_message(WidgetMessage::height( - interface.root, - MessageDirection::ToWidget, - size.height, - )); + let fps = ctx.renderer.get_statistics().frames_per_second; + let debug_text = format!( + "Example 07 - Sound\n[W][S][A][D] - walk, [SPACE] - jump.\n\ + FPS: {}\nUse [1][2][3][4] to select graphics quality.", + fps + ); + game.engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + debug_text, + )); } + + // It is very important to "pump" messages from UI. Even if don't need to + // respond to such message, you should call this method, otherwise UI + // might behave very weird. + while let Some(_ui_event) = game.engine.user_interface.poll_message() { + // ************************ + // Put your data model synchronization code here. It should + // take message and update data in your game according to + // changes in UI. + // ************************ + } + + game.engine.update( + fixed_timestep, + control_flow, + &mut lag, + Default::default(), + ); + + lag -= fixed_timestep; } - WindowEvent::KeyboardInput { event: input, .. } => { - // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene.player.handle_key_event(input, fixed_timestep); - } - let settings = match input.physical_key { - KeyCode::Digit1 => Some(QualitySettings::ultra()), - KeyCode::Digit2 => Some(QualitySettings::high()), - KeyCode::Digit3 => Some(QualitySettings::medium()), - KeyCode::Digit4 => Some(QualitySettings::low()), - _ => None, - }; + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { + ctx.window.request_redraw(); + } + } + Event::Resumed => { + game.engine + .initialize_graphics_context(window_target) + .unwrap(); + } + Event::Suspended => { + game.engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + game.engine.render().unwrap(); + } + Event::LoopExiting => { + println!("{:?}", fyrox::core::profiler::print()); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = game.engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } - if let Some(settings) = settings { - if let GraphicsContext::Initialized(ref mut ctx) = + // Root UI node should be resized too, otherwise progress bar will stay + // in wrong position after resize. + if let GraphicsContext::Initialized(ref ctx) = game.engine.graphics_context { - ctx.renderer - .set_quality_settings(&fix_shadows_distance(settings)) - .unwrap(); + let size = size.to_logical(ctx.window.scale_factor()); + game.engine + .user_interface + .send_message(WidgetMessage::width( + interface.root, + MessageDirection::ToWidget, + size.width, + )); + game.engine + .user_interface + .send_message(WidgetMessage::height( + interface.root, + MessageDirection::ToWidget, + size.height, + )); } } - } - _ => (), - } + WindowEvent::KeyboardInput { event: input, .. } => { + // Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32) + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene.player.handle_key_event(input, fixed_timestep); + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - game.engine.user_interface.process_os_event(&os_event); + let settings = match input.physical_key { + KeyCode::Digit1 => Some(QualitySettings::ultra()), + KeyCode::Digit2 => Some(QualitySettings::high()), + KeyCode::Digit3 => Some(QualitySettings::medium()), + KeyCode::Digit4 => Some(QualitySettings::low()), + _ => None, + }; + + if let Some(settings) = settings { + if let GraphicsContext::Initialized(ref mut ctx) = + game.engine.graphics_context + { + ctx.renderer + .set_quality_settings(&fix_shadows_distance(settings)) + .unwrap(); + } + } + } + _ => (), + } + + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + game.engine.user_interface.process_os_event(&os_event); + } } - } - Event::DeviceEvent { event, .. } => { - if let Some(game_scene) = game.game_scene.as_mut() { - game_scene - .player - .handle_device_event(&event, fixed_timestep); + Event::DeviceEvent { event, .. } => { + if let Some(game_scene) = game.game_scene.as_mut() { + game_scene + .player + .handle_device_event(&event, fixed_timestep); + } } + _ => *control_flow = ControlFlow::Poll, } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/examples/terrain.rs b/examples/terrain.rs index 27e96ee22..d8c2c0172 100644 --- a/examples/terrain.rs +++ b/examples/terrain.rs @@ -3,6 +3,7 @@ pub mod shared; use crate::shared::create_camera; +use fyrox::event_loop::EventLoop; use fyrox::resource::texture::Texture; use fyrox::{ asset::manager::ResourceManager, @@ -277,7 +278,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Terrain".to_string(), diff --git a/examples/textbox.rs b/examples/textbox.rs index a6a3c17a5..d04dd458d 100644 --- a/examples/textbox.rs +++ b/examples/textbox.rs @@ -1,3 +1,4 @@ +use fyrox::event_loop::EventLoop; use fyrox::{ core::{algebra::Vector2, color::Color, pool::Handle}, engine::{executor::Executor, GraphicsContextParams}, @@ -41,7 +42,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - Default::default(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - Text Box".to_string(), diff --git a/examples/trasparent.rs b/examples/trasparent.rs index 54ca1f227..f5255dc83 100644 --- a/examples/trasparent.rs +++ b/examples/trasparent.rs @@ -90,7 +90,7 @@ impl PluginConstructor for GameConstructor { fn main() { let mut executor = Executor::from_params( - EventLoop::new(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { transparent: true, diff --git a/examples/ui.rs b/examples/ui.rs index 7cd9caa63..b2b1c880b 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -323,7 +323,7 @@ async fn create_scene(resource_manager: ResourceManager) -> GameScene { } fn main() { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let graphics_context_params = GraphicsContextParams { window_attributes: WindowAttributes { title: "Example - User Interface".to_string(), @@ -365,154 +365,160 @@ fn main() { // Finally run our event loop which will respond to OS and window events and update // engine state accordingly. Engine lets you to decide which event should be handled, // this is minimal working example if how it should be. - event_loop.run(move |event, window_target, control_flow| { - match event { - Event::MainEventsCleared => { - // This main game loop - it has fixed time step which means that game - // code will run at fixed speed even if renderer can't give you desired - // 60 fps. - let elapsed = previous.elapsed(); - previous = Instant::now(); - lag += elapsed.as_secs_f32(); - while lag >= fixed_timestep { - // ************************ - // Put your game logic here. - // ************************ + event_loop + .run(move |event, window_target, control_flow| { + match event { + Event::AboutToWait => { + // This main game loop - it has fixed time step which means that game + // code will run at fixed speed even if renderer can't give you desired + // 60 fps. + let elapsed = previous.elapsed(); + previous = Instant::now(); + lag += elapsed.as_secs_f32(); + while lag >= fixed_timestep { + // ************************ + // Put your game logic here. + // ************************ - // Use stored scene handle to borrow a mutable reference of scene in - // engine. - let scene = &mut engine.scenes[scene_handle]; + // Use stored scene handle to borrow a mutable reference of scene in + // engine. + let scene = &mut engine.scenes[scene_handle]; - scene.graph[model_handle] - .local_transform_mut() - .set_scale(Vector3::new(model_scale, model_scale, model_scale)) - .set_rotation(UnitQuaternion::from_axis_angle( - &Vector3::y_axis(), - model_angle.to_radians(), - )); + scene.graph[model_handle] + .local_transform_mut() + .set_scale(Vector3::new(model_scale, model_scale, model_scale)) + .set_rotation(UnitQuaternion::from_axis_angle( + &Vector3::y_axis(), + model_angle.to_radians(), + )); - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - let fps = ctx.renderer.get_statistics().frames_per_second; - engine.user_interface.send_message(TextMessage::text( - interface.debug_text, - MessageDirection::ToWidget, - format!("Example 04 - User Interface\nFPS: {}", fps), - )); + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + let fps = ctx.renderer.get_statistics().frames_per_second; + engine.user_interface.send_message(TextMessage::text( + interface.debug_text, + MessageDirection::ToWidget, + format!("Example 04 - User Interface\nFPS: {}", fps), + )); + } + + engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); + lag -= fixed_timestep; } - engine.update(fixed_timestep, control_flow, &mut lag, Default::default()); - lag -= fixed_timestep; - } - - // It is very important to "pump" messages from UI. This our main point where we communicate - // with user interface. As you saw earlier, there is no callbacks on UI elements, instead we - // use messages to get information from UI elements. This provides perfect decoupling of logic - // from UI elements and works well with borrow checker. - while let Some(ui_message) = engine.user_interface.poll_message() { - if let Some(ScrollBarMessage::Value(value)) = - ui_message.data::() - { - if ui_message.direction() == MessageDirection::FromWidget { - // Some of our scroll bars has changed its value. Check which one. - // Each message has source - a handle of UI element that created this message. - // It is used to understand from which UI element message has come. - if ui_message.destination() == interface.scale { - model_scale = *value; - } else if ui_message.destination() == interface.yaw { - model_angle = *value; + // It is very important to "pump" messages from UI. This our main point where we communicate + // with user interface. As you saw earlier, there is no callbacks on UI elements, instead we + // use messages to get information from UI elements. This provides perfect decoupling of logic + // from UI elements and works well with borrow checker. + while let Some(ui_message) = engine.user_interface.poll_message() { + if let Some(ScrollBarMessage::Value(value)) = + ui_message.data::() + { + if ui_message.direction() == MessageDirection::FromWidget { + // Some of our scroll bars has changed its value. Check which one. + // Each message has source - a handle of UI element that created this message. + // It is used to understand from which UI element message has come. + if ui_message.destination() == interface.scale { + model_scale = *value; + } else if ui_message.destination() == interface.yaw { + model_angle = *value; + } } - } - } else if let Some(ButtonMessage::Click) = ui_message.data::() { - // Once we received Click event from Reset button, we have to reset angle and scale - // of model. To do that we borrow each UI element in engine and set its value directly. - // This is not ideal because there is tight coupling between UI code and model values, - // but still good enough for example. - if ui_message.destination() == interface.reset { - engine.user_interface.send_message(ScrollBarMessage::value( - interface.scale, - MessageDirection::ToWidget, - DEFAULT_MODEL_SCALE, - )); - engine.user_interface.send_message(ScrollBarMessage::value( - interface.yaw, - MessageDirection::ToWidget, - DEFAULT_MODEL_ROTATION, - )); - } - } else if let Some(DropdownListMessage::SelectionChanged(Some(idx))) = - ui_message.data::() - { - // Video mode has changed and we must change video mode to what user wants. - if ui_message.destination() == interface.resolutions { - let video_mode = interface.video_modes.get(*idx).unwrap(); + } else if let Some(ButtonMessage::Click) = + ui_message.data::() + { + // Once we received Click event from Reset button, we have to reset angle and scale + // of model. To do that we borrow each UI element in engine and set its value directly. + // This is not ideal because there is tight coupling between UI code and model values, + // but still good enough for example. + if ui_message.destination() == interface.reset { + engine.user_interface.send_message(ScrollBarMessage::value( + interface.scale, + MessageDirection::ToWidget, + DEFAULT_MODEL_SCALE, + )); + engine.user_interface.send_message(ScrollBarMessage::value( + interface.yaw, + MessageDirection::ToWidget, + DEFAULT_MODEL_ROTATION, + )); + } + } else if let Some(DropdownListMessage::SelectionChanged(Some(idx))) = + ui_message.data::() + { + // Video mode has changed and we must change video mode to what user wants. + if ui_message.destination() == interface.resolutions { + let video_mode = interface.video_modes.get(*idx).unwrap(); - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.set_fullscreen(Some(Fullscreen::Exclusive( - video_mode.clone(), - ))); + if let GraphicsContext::Initialized(ref ctx) = + engine.graphics_context + { + ctx.window.set_fullscreen(Some(Fullscreen::Exclusive( + video_mode.clone(), + ))); - // Due to some weird bug in winit it does not send Resized event. - if let Err(e) = engine.set_frame_size(( - video_mode.size().width, - video_mode.size().height, - )) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); + // Due to some weird bug in winit it does not send Resized event. + if let Err(e) = engine.set_frame_size(( + video_mode.size().width, + video_mode.size().height, + )) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } } } } } - } - // Rendering must be explicitly requested and handled after RedrawRequested event is received. - if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { - ctx.window.request_redraw(); - } - } - Event::Resumed => { - engine.initialize_graphics_context(window_target).unwrap(); - } - Event::Suspended => { - engine.destroy_graphics_context().unwrap(); - } - Event::RedrawRequested(_) => { - // Run renderer at max speed - it is not tied to game code. - engine.render().unwrap(); - } - Event::WindowEvent { event, .. } => { - match &event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(size) => { - // It is very important to handle Resized event from window, because - // renderer knows nothing about window size - it must be notified - // directly when window size has changed. - if let Err(e) = engine.set_frame_size((*size).into()) { - Log::writeln( - MessageKind::Error, - format!("Unable to set frame size: {:?}", e), - ); - } + // Rendering must be explicitly requested and handled after RedrawRequested event is received. + if let GraphicsContext::Initialized(ref ctx) = engine.graphics_context { + ctx.window.request_redraw(); } - WindowEvent::KeyboardInput { event: input, .. } => { - if input.state == ElementState::Pressed - && input.physical_key == KeyCode::Escape - { - *control_flow = ControlFlow::Exit; - } - } - _ => (), } + Event::Resumed => { + engine.initialize_graphics_context(window_target).unwrap(); + } + Event::Suspended => { + engine.destroy_graphics_context().unwrap(); + } + Event::RedrawRequested(_) => { + // Run renderer at max speed - it is not tied to game code. + engine.render().unwrap(); + } + Event::WindowEvent { event, .. } => { + match &event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(size) => { + // It is very important to handle Resized event from window, because + // renderer knows nothing about window size - it must be notified + // directly when window size has changed. + if let Err(e) = engine.set_frame_size((*size).into()) { + Log::writeln( + MessageKind::Error, + format!("Unable to set frame size: {:?}", e), + ); + } + } + WindowEvent::KeyboardInput { event: input, .. } => { + if input.state == ElementState::Pressed + && input.physical_key == KeyCode::Escape + { + *control_flow = ControlFlow::Exit; + } + } + _ => (), + } - // It is very important to "feed" user interface (UI) with events coming - // from main window, otherwise UI won't respond to mouse, keyboard, or any - // other event. - if let Some(os_event) = translate_event(&event) { - engine.user_interface.process_os_event(&os_event); + // It is very important to "feed" user interface (UI) with events coming + // from main window, otherwise UI won't respond to mouse, keyboard, or any + // other event. + if let Some(os_event) = translate_event(&event) { + engine.user_interface.process_os_event(&os_event); + } } + _ => *control_flow = ControlFlow::Poll, } - _ => *control_flow = ControlFlow::Poll, - } - }); + }) + .unwrap(); } diff --git a/src/engine/executor.rs b/src/engine/executor.rs index 98a6e1b5e..c2bea90a8 100644 --- a/src/engine/executor.rs +++ b/src/engine/executor.rs @@ -10,10 +10,11 @@ use crate::{ Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext, }, event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, plugin::PluginConstructor, scene::loader::AsyncSceneLoader, utils::translate_event, + window::WindowAttributes, }; use clap::Parser; use std::{ @@ -21,7 +22,6 @@ use std::{ sync::Arc, time::Duration, }; -use winit::window::WindowAttributes; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -90,7 +90,7 @@ impl Executor { /// way to create an executor see [`Executor::from_params`]. pub fn new() -> Self { Self::from_params( - EventLoop::new(), + EventLoop::new().unwrap(), GraphicsContextParams { window_attributes: WindowAttributes { resizable: true, @@ -132,8 +132,8 @@ impl Executor { self.engine.add_plugin_constructor(plugin) } - /// Runs the executor - starts your game. This function is never returns. - pub fn run(mut self) -> ! { + /// Runs the executor - starts your game. + pub fn run(mut self) { let mut engine = self.engine; let event_loop = self.event_loop; let headless = self.headless; @@ -156,7 +156,9 @@ impl Executor { let fixed_time_step = 1.0 / self.desired_update_rate; let mut lag = 0.0; - event_loop.run(move |event, window_target, control_flow| { + run_executor(event_loop, move |event, window_target, control_flow| { + control_flow.set_wait_timeout(Duration::from_millis(16)); + engine.handle_os_event_by_plugins(&event, fixed_time_step, control_flow, &mut lag); let scenes = engine @@ -196,7 +198,7 @@ impl Executor { &mut lag, ); } - Event::MainEventsCleared => { + Event::AboutToWait => { prepare_control_flow(control_flow, fixed_time_step); if let Some(loader) = self.loader.as_ref() { @@ -273,3 +275,19 @@ fn prepare_control_flow(control_flow: &mut ControlFlow, #[allow(unused_variables control_flow.set_poll(); } } + +fn run_executor(event_loop: EventLoop, callback: F) +where + F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) + 'static, +{ + #[cfg(target_arch = "wasm32")] + { + use winit::platform::web::EventLoopExtWebSys; + event_loop.spawn(callback); + } + + #[cfg(not(target_arch = "wasm32"))] + { + event_loop.run(callback).unwrap(); + } +}