Update wgpu to 0.20.1 and winit to 0.30.3 #26

Merged
SeanOMik merged 7 commits from chore/winit-wgpu-update into main 2024-09-23 23:06:31 +00:00
39 changed files with 7232 additions and 1647 deletions
Showing only changes of commit 45fd190409 - Show all commits

2658
Cargo.lock generated

File diff suppressed because it is too large Load Diff

4999
Cargo.lock.old Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ use tracing::info;
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
@ -105,7 +105,7 @@ async fn main() {
.await;
}
fn setup_scene_plugin(game: &mut Game) {
fn setup_scene_plugin(app: &mut App) {
let world = game.world_mut();
let resman = world.get_resource_mut::<ResourceManager>();
let camera_gltf = resman

View File

@ -18,7 +18,7 @@ use lyra_engine::{
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
@ -99,7 +99,7 @@ async fn main() {
.await;
}
fn setup_scene_plugin(game: &mut Game) {
fn setup_scene_plugin(app: &mut App) {
let world = game.world_mut();
let resman = world.get_resource_mut::<ResourceManager>();
let camera_gltf = resman
@ -136,7 +136,7 @@ fn setup_scene_plugin(game: &mut Game) {
world.spawn((camera, FreeFlyCamera::default()));
}
fn setup_script_plugin(game: &mut Game) {
fn setup_script_plugin(app: &mut App) {
game.with_plugin(LuaScriptingPlugin);
let world = game.world_mut();

View File

@ -21,7 +21,7 @@ const POINT_LIGHT_MIN_Z: f32 = -5.0;
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
@ -80,7 +80,7 @@ async fn main() {
.run().await;
}
fn setup_scene_plugin(game: &mut Game) {
fn setup_scene_plugin(app: &mut App) {
let fps_counter = |mut counter: ResMut<fps_counter::FPSCounter>, delta: Res<DeltaTime>| -> anyhow::Result<()> {
let tick = counter.tick();
@ -174,7 +174,7 @@ fn setup_scene_plugin(game: &mut Game) {
world.spawn(( camera, FreeFlyCamera::default() ));
}
fn camera_debug_plugin(game: &mut Game) {
fn camera_debug_plugin(app: &mut App) {
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
if handler.was_action_just_pressed("Debug") {
for mut cam in view.into_iter() {

View File

@ -19,7 +19,7 @@ use lyra_engine::{
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
@ -99,7 +99,7 @@ async fn main() {
.await;
}
fn setup_scene_plugin(game: &mut Game) {
fn setup_scene_plugin(app: &mut App) {
let world = game.world_mut();
let resman = world.get_resource_mut::<ResourceManager>();

View File

@ -1,6 +1,6 @@
use lyra_engine::{
assets::{gltf::Gltf, ResourceManager},
game::Game,
game::App,
input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
@ -16,7 +16,7 @@ use lyra_engine::{
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
@ -31,61 +31,61 @@ async fn main() {
.bind(
ACTLBL_MOVE_FORWARD_BACKWARD,
&[
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::KeyW).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::KeyS).into_binding_modifier(-1.0),
],
)
.bind(
ACTLBL_MOVE_LEFT_RIGHT,
&[
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::KeyA).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::KeyD).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_MOVE_UP_DOWN,
&[
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::KeyC).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::KeyZ).into_binding_modifier(-1.0),
],
)
.bind(
ACTLBL_LOOK_LEFT_RIGHT,
&[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_LOOK_UP_DOWN,
&[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_LOOK_ROLL,
&[
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::KeyE).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::KeyQ).into_binding_modifier(1.0),
],
)
.bind(
"Debug",
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
&[ActionSource::Keyboard(KeyCode::KeyB).into_binding()],
)
.finish(),
)
.finish();
let world = game.world_mut();
let world = app.world_mut();
world.add_resource(action_handler);
game.with_plugin(InputActionPlugin);
app.with_plugin(InputActionPlugin);
};
Game::initialize()
App::initialize()
.await
.with_plugin(lyra_engine::DefaultPlugins)
.with_plugin(setup_scene_plugin)
@ -96,7 +96,7 @@ async fn main() {
.await;
}
fn setup_scene_plugin(game: &mut Game) {
fn setup_scene_plugin(app: &mut App) {
let world = game.world_mut();
let resman = world.get_resource_mut::<ResourceManager>();

View File

@ -260,7 +260,7 @@ async fn main() {
Ok(())
};
let camera_debug_plugin = move |game: &mut Game| {
let camera_debug_plugin = move |app: &mut App| {
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
if handler.was_action_just_pressed("Debug") {
for mut cam in view.into_iter() {
@ -275,7 +275,7 @@ async fn main() {
game.with_system("update_world_transforms", scene::system_update_world_transforms, &[]);
};
let action_handler_plugin = |game: &mut Game| {
let action_handler_plugin = |app: &mut App| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
@ -327,7 +327,7 @@ async fn main() {
game.with_plugin(InputActionPlugin);
};
/* let script_test_plugin = |game: &mut Game| {
/* let script_test_plugin = |app: &mut App| {
game.with_plugin(LuaScriptingPlugin);
let world = game.world_mut();

View File

@ -197,11 +197,9 @@ mod tests {
let mut view_iter = view.into_iter();
while let Some((_e, view_row)) = view_iter.next(&world) {
assert_eq!(view_row.len(), 1);
let mut row_iter = view_row.row.iter();
let mut row_iter = view_row.iter();
let dynamic_type = row_iter.next().unwrap();
let component_data = unsafe { dynamic_type.ptr.cast::<u32>().as_ref() };
assert_eq!(*component_data, 50);
}
@ -226,11 +224,9 @@ mod tests {
for (_e, view_row) in view.into_iter() {
assert_eq!(view_row.len(), 1);
let mut row_iter = view_row.iter();
let mut row_iter = view_row.row.iter();
let dynamic_type = row_iter.next().unwrap();
let dynamic_type = row_iter.next().unwrap();
let component_data = unsafe { dynamic_type.ptr.cast::<u32>().as_ref() };
assert_eq!(*component_data, 50);
}

View File

@ -12,31 +12,30 @@ lyra-math = { path = "../lyra-math" }
lyra-scene = { path = "../lyra-scene" }
wgsl_preprocessor = { path = "../wgsl-preprocessor" }
winit = "0.28.1"
wgpu = { version = "0.15.1", features = [ "expose-ids"] }
winit = "0.30.5"
wgpu = { version = "22.1.0" }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
tracing-log = "0.1.3"
tracing-log = "0.2.0"
tracing-appender = "0.2.2"
tracing-tracy = { version = "0.11.0", optional = true }
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
cfg-if = "1"
bytemuck = { version = "1.12", features = [ "derive", "min_const_generics" ] }
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
image = "0.25.2"
anyhow = "1.0"
instant = "0.1"
async-trait = "0.1.65"
glam = { version = "0.24.0", features = ["bytemuck", "debug-glam-assert"] }
gilrs-core = "0.5.6"
glam = { version = "0.29.0", features = ["bytemuck", "debug-glam-assert"] }
syn = "2.0.26"
quote = "1.0.29"
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
itertools = "0.11.0"
itertools = "0.13.0"
thiserror = "1.0.56"
unique = "0.9.1"
rustc-hash = "1.1.0"
rustc-hash = "2.0.0"
petgraph = { version = "0.6.5", features = ["matrix_graph"] }
bind_match = "0.1.2"
round_mult = "0.1.3"

View File

@ -42,8 +42,8 @@ pub fn delta_time_system(world: &mut World) -> anyhow::Result<()> {
pub struct DeltaTimePlugin;
impl Plugin for DeltaTimePlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.world_mut().add_resource(DeltaTime(0.0, None));
game.add_system_to_stage(GameStages::First, "delta_time", delta_time_system, &[]);
fn setup(&self, app: &mut crate::game::App) {
app.world.add_resource(DeltaTime(0.0, None));
app.add_system_to_stage(GameStages::First, "delta_time", delta_time_system, &[]);
}
}

View File

@ -196,7 +196,7 @@ impl<E: Event> Iterator for EventReader<E> {
pub struct EventsPlugin;
impl Plugin for EventsPlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.world_mut().add_resource(EventQueue::new());
fn setup(&self, app: &mut crate::game::App) {
app.world.add_resource(EventQueue::new());
}
}

View File

@ -1,9 +1,9 @@
use std::{sync::Arc, collections::VecDeque, ptr::NonNull};
use std::{cell::OnceCell, collections::VecDeque, ptr::NonNull, sync::Arc};
use async_std::task::block_on;
use lyra_ecs::{World, system::{System, IntoSystem}};
use tracing::{error, info, Level};
use lyra_ecs::{system::{IntoSystem, System}, ResourceObject, World};
use lyra_math::{IVec2, Vec2};
use rustc_hash::FxHashMap;
use tracing::{debug, debug_span, info, Level};
use tracing_appender::non_blocking;
use tracing_subscriber::{
layer::SubscriberExt,
@ -11,7 +11,7 @@ use tracing_subscriber::{
util::SubscriberInitExt, fmt,
};
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
use winit::{application::ApplicationHandler, event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, NamedKey}, window::{Window, WindowId}};
use crate::{render::{renderer::{Renderer, BasicRenderer}, window::WindowOptions}, input::InputEvent, plugin::Plugin, change_tracker::Ct, EventQueue, StagedExecutor, Stage};
@ -37,8 +37,13 @@ pub struct Controls<'a> {
#[derive(Clone, Default)]
pub struct WindowState {
pub is_focused: bool,
pub is_cursor_inside_window: bool,
/// Indicates if the window is currently focused.
pub focused: bool,
/// Indicates if the window is currently occluded.
pub occluded: bool,
/// Indicates if the cursor is inside of the window.
pub cursor_inside_window: bool,
pub position: IVec2,
}
impl WindowState {
@ -47,216 +52,41 @@ impl WindowState {
}
}
struct GameLoop {
window: Arc<Window>,
renderer: Box<dyn Renderer>,
world: World,
pub struct App {
windows: FxHashMap<WindowId, Arc<Window>>,
renderer: OnceCell<Box<dyn Renderer>>,
pub world: World,
plugins: VecDeque<Box<dyn Plugin>>,
startup_systems: VecDeque<Box<dyn System>>,
staged_exec: StagedExecutor,
run_fn: OnceCell<Box<dyn FnOnce(App)>>,
}
impl GameLoop {
pub async fn new(window: Arc<Window>, mut world: World, staged_exec: StagedExecutor) -> Self {
let renderer = BasicRenderer::create_with_window(&mut world, window.clone()).await;
impl App {
pub fn new() -> Self {
Self {
window: Arc::clone(&window),
renderer: Box::new(renderer),
world,
staged_exec,
windows: FxHashMap::default(),
renderer: OnceCell::new(),
world: World::new(),
plugins: Default::default(),
startup_systems: Default::default(),
staged_exec: StagedExecutor::new(),
run_fn: OnceCell::new(),
}
}
pub async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.renderer.on_resize(&mut self.world, new_size);
}
pub async fn on_init(&mut self) {
// Create the EventQueue resource in the world
self.world.add_resource(self.window.clone());
}
pub fn run_sync(&mut self, event: Event<()>, control_flow: &mut ControlFlow) {
block_on(self.run_event_loop(event, control_flow))
}
async fn update(&mut self) {
let world_ptr = NonNull::from(&self.world);
if let Err(e) = self.staged_exec.execute(world_ptr, true) {
error!("Error when executing staged systems: '{}'", e);
}
}
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
match event {
InputEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => {
self.on_exit();
Some(ControlFlow::Exit)
},
_ => {
//debug!("Got unhandled input event: \"{:?}\"", event);
None
}
}
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.renderer.get_mut()
.expect("renderer was not initialized")
.on_resize(&mut self.world, new_size);
}
fn on_exit(&mut self) {
info!("On exit!");
}
pub async fn run_event_loop(&mut self, event: Event<'_, ()>, control_flow: &mut ControlFlow) {
*control_flow = ControlFlow::Poll;
match event {
Event::DeviceEvent { device_id, event: DeviceEvent::MouseMotion { delta } } => {
//debug!("motion: {delta:?}");
// convert a MouseMotion event to an InputEvent
// make sure that the mouse is inside the window and the mouse has focus before reporting mouse motion
/* let trigger = matches!(self.world.get_resource::<WindowState>(), Some(window_state)
if window_state.is_focused && window_state.is_cursor_inside_window); */
let trigger = matches!(self.world.try_get_resource::<Ct<WindowOptions>>(), Some(window)
if window.focused && window.cursor_inside_window);
if trigger {
let mut event_queue = self.world.try_get_resource_mut::<EventQueue>().unwrap();
let input_event = InputEvent::MouseMotion { device_id, delta, };
event_queue.trigger_event(input_event);
}
},
Event::WindowEvent {
ref event,
window_id,
} if window_id == self.window.id() => {
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Ok(input_event) = InputEvent::try_from(event) {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
{
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_event.clone());
};
}
if let Some(new_control) = self.input_update(&input_event).await {
*control_flow = new_control;
}
} else {
match event {
WindowEvent::CloseRequested => {
self.on_exit();
*control_flow = ControlFlow::Exit
},
WindowEvent::Resized(physical_size) => {
self.on_resize(*physical_size).await;
},
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
self.on_resize(**new_inner_size).await;
},
WindowEvent::Focused(is_focused) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.is_focused = *is_focused;
},
_ => {}
}
}
},
Event::RedrawRequested(window_id) if window_id == self.window.id() => {
// Update the world
self.update().await;
/* self.fps_counter.tick();
if let Some(fps) = self.fps_counter.get_change() {
debug!("FPS: {}fps, {:.2}ms/frame", fps, self.fps_counter.get_tick_time());
} */
self.renderer.as_mut().prepare(&mut self.world);
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.update_events();
}
match self.renderer.as_mut().render() {
Ok(_) => {}
// Reconfigure the surface if lost
Err(wgpu::SurfaceError::Lost) => self.on_resize(self.renderer.as_ref().surface_size()).await,
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e),
}
},
Event::MainEventsCleared => {
self.window.request_redraw();
},
_ => {}
}
}
}
pub struct Game {
world: Option<World>,
plugins: VecDeque<Box<dyn Plugin>>,
system_exec: Option<StagedExecutor>,
startup_systems: VecDeque<Box<dyn System>>,
}
impl Default for Game {
fn default() -> Self {
let mut staged = StagedExecutor::new();
staged.add_stage(GameStages::First);
staged.add_stage_after(GameStages::First, GameStages::PreUpdate);
staged.add_stage_after(GameStages::PreUpdate, GameStages::Update);
staged.add_stage_after(GameStages::Update, GameStages::PostUpdate);
staged.add_stage_after(GameStages::PostUpdate, GameStages::Last);
Self {
world: Some(World::new()),
plugins: VecDeque::new(),
system_exec: Some(staged),
startup_systems: VecDeque::new(),
}
}
}
impl Game {
pub async fn initialize() -> Game {
Self::default()
}
/// Get the world of this game
pub fn world_mut(&mut self) -> &mut World {
// world is always `Some`, so unwrapping is safe
self.world.as_mut().unwrap()
}
/// Get the world of this game
pub fn world(&self) -> &World {
// world is always `Some`, so unwrapping is safe
self.world.as_ref().unwrap()
pub fn add_resource<T: ResourceObject>(&mut self, data: T) {
self.world.add_resource(data);
}
/// Add a system to the ecs world
@ -265,8 +95,7 @@ impl Game {
S: IntoSystem<A>,
<S as IntoSystem<A>>::System: 'static
{
let system_dispatcher = self.system_exec.as_mut().unwrap();
system_dispatcher.add_system_to_stage(GameStages::Update, name, system.into_system(), depends);
self.staged_exec.add_system_to_stage(GameStages::Update, name, system.into_system(), depends);
self
}
@ -275,8 +104,7 @@ impl Game {
///
/// This stage could run at any moment if nothing is dependent on it.
pub fn add_stage<T: Stage>(&mut self, stage: T) -> &mut Self {
let system_dispatcher = self.system_exec.as_mut().unwrap();
system_dispatcher.add_stage(stage);
self.staged_exec.add_stage(stage);
self
}
@ -287,8 +115,7 @@ impl Game {
/// * `before` - The stage that will run before `after`.
/// * `after` - The stage that will run after `before`.
pub fn add_stage_after<T: Stage, U: Stage>(&mut self, before: T, after: U) -> &mut Self {
let system_dispatcher = self.system_exec.as_mut().unwrap();
system_dispatcher.add_stage_after(before, after);
self.staged_exec.add_stage_after(before, after);
self
}
@ -304,8 +131,7 @@ impl Game {
S: IntoSystem<A>,
<S as IntoSystem<A>>::System: 'static
{
let system_dispatcher = self.system_exec.as_mut().unwrap();
system_dispatcher.add_system_to_stage(stage, name, system.into_system(), depends);
self.staged_exec.add_system_to_stage(stage, name, system.into_system(), depends);
self
}
@ -337,53 +163,85 @@ impl Game {
///
/// This isn't recommended, you should create a startup system and add it to `with_startup_system`
pub fn with_world(&mut self, world: World) -> &mut Self {
self.world = Some(world);
self.world = world;
self
}
/// Start the game
pub async fn run(&mut self) {
// init logging
let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout());
{
let t = tracing_subscriber::registry()
.with(fmt::layer().with_writer(stdout_layer));
#[cfg(feature = "tracy")]
let t = t.with(tracing_tracy::TracyLayer::default());
t.with(filter::Targets::new()
// done by prefix, so it includes all lyra subpackages
.with_target("lyra", Level::DEBUG)
.with_target("wgsl_preprocessor", Level::DEBUG)
.with_target("wgpu", Level::WARN)
.with_target("winit", Level::DEBUG)
.with_default(Level::INFO))
.init();
}
let world = self.world.take().unwrap_or_default();
// run startup systems
while let Some(mut startup) = self.startup_systems.pop_front() {
let startup = startup.as_mut();
let world_ptr = NonNull::from(&world);
startup.setup(world_ptr).expect("World returned an error!");
startup.execute(world_ptr).expect("World returned an error!");
}
// start winit event loops
let event_loop = EventLoop::new();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
let system_dispatcher = self.system_exec.take().unwrap();
let mut g_loop = GameLoop::new(Arc::clone(&window), world, system_dispatcher).await;
g_loop.on_init().await;
event_loop.run(move |event, _, control_flow| {
g_loop.run_sync(event, control_flow);
});
fn set_run_fn<F>(&self, f: F)
where
F: FnOnce(App) + 'static
{
// ignore if a runner function was already set
let _ = self.run_fn.set(Box::new(f));
}
}
fn run(mut self) {
let f = self.run_fn.take()
.expect("No run function set");
f(self);
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
debug!("Resumed");
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
//let _e = debug_span!("window_event", window=window_id).entered();
let window = match self.windows.get_mut(&window_id) {
Some(w) => w,
None => return,
};
// If try_from failed, that means that the WindowEvent is not an
// input related event.
if let Some(input_ev) = InputEvent::from_window_event(&event) {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_ev.clone());
}
} else {
match event {
WindowEvent::ActivationTokenDone { serial, token } => todo!(),
WindowEvent::Resized(physical_size) => {
self.on_resize(physical_size);
},
WindowEvent::Moved(physical_position) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.position = IVec2::new(physical_position.x, physical_position.y);
},
WindowEvent::CloseRequested => {
self.on_exit();
event_loop.exit();
},
WindowEvent::Destroyed => todo!(),
WindowEvent::DroppedFile(path_buf) => todo!(),
WindowEvent::HoveredFile(path_buf) => todo!(),
WindowEvent::HoveredFileCancelled => todo!(),
WindowEvent::Focused(focused) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.focused = focused;
},
WindowEvent::ModifiersChanged(modifiers) => todo!(),
WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer } => {
info!("changed scale to {scale_factor}");
},
WindowEvent::ThemeChanged(theme) => todo!(),
WindowEvent::Occluded(occ) => {
let mut state = self.world.get_resource_or_else(WindowState::new);
state.occluded = occ;
},
WindowEvent::RedrawRequested => todo!(),
_ => {}
}
}
}
}

View File

@ -597,7 +597,7 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
pub struct InputActionPlugin;
impl Plugin for InputActionPlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.add_system_to_stage(GameStages::PreUpdate, "input_actions", actions_system, &[]);
fn setup(&self, app: &mut crate::game::App) {
app.add_system_to_stage(GameStages::PreUpdate, "input_actions", actions_system, &[]);
}
}

View File

@ -88,6 +88,8 @@ pub enum MouseButton {
Left,
Right,
Middle,
Back,
Forward,
Other(u16),
}

View File

@ -1,4 +1,4 @@
use winit::{event::{DeviceId, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, MouseButton, AxisId, Touch, WindowEvent, ElementState}, dpi::PhysicalPosition};
use winit::{dpi::PhysicalPosition, event::{AxisId, DeviceId, ElementState, KeyEvent, MouseButton, MouseScrollDelta, Touch, TouchPhase, WindowEvent}};
/// Wrapper around events from `winit::WindowEvent` that are specific to input related events.
///
@ -19,7 +19,7 @@ pub enum InputEvent {
/// An event from the keyboard has been received.
KeyboardInput {
device_id: DeviceId,
input: KeyboardInput,
event: KeyEvent,
/// If true, the event was generated synthetically by winit in one of the following circumstances:
/// Synthetic key press events are generated for all keys pressed when a window gains focus.
@ -28,20 +28,10 @@ pub enum InputEvent {
is_synthetic: bool,
},
/// Change in physical position of a pointing device.
/// This represents raw, unfiltered physical motion.
///
/// This is from winit's `DeviceEvent::MouseMotion`
MouseMotion {
device_id: DeviceId,
delta: (f64, f64),
},
/// The cursor has moved on the window.
CursorMoved {
device_id: DeviceId,
position: PhysicalPosition<f64>,
modifiers: ModifiersState,
},
/// The cursor has entered the window.
@ -59,8 +49,6 @@ pub enum InputEvent {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
/// Deprecated in favor of WindowEvent::ModifiersChanged
modifiers: ModifiersState,
},
/// An mouse button press has been received.
@ -68,51 +56,6 @@ pub enum InputEvent {
device_id: DeviceId,
state: ElementState,
button: MouseButton,
#[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
modifiers: ModifiersState,
},
/// Touchpad magnification event with two-finger pinch gesture.
/// Positive delta values indicate magnification (zooming in) and negative delta values indicate shrinking (zooming out).
///
/// Note: Only available on macOS
TouchpadMagnify {
device_id: DeviceId,
delta: f64,
phase: TouchPhase,
},
/// Smart magnification event.
///
/// On a Mac, smart magnification is triggered by a double tap with two fingers
/// on the trackpad and is commonly used to zoom on a certain object
/// (e.g. a paragraph of a PDF) or (sort of like a toggle) to reset any zoom.
/// The gesture is also supported in Safari, Pages, etc.
///
/// The event is general enough that its generating gesture is allowed to vary
/// across platforms. It could also be generated by another device.
///
/// Unfortunatly, neither [Windows](https://support.microsoft.com/en-us/windows/touch-gestures-for-windows-a9d28305-4818-a5df-4e2b-e5590f850741)
/// nor [Wayland](https://wayland.freedesktop.org/libinput/doc/latest/gestures.html)
/// support this gesture or any other gesture with the same effect.
///
/// ## Platform-specific
///
/// - Only available on **macOS 10.8** and later.
SmartMagnify { device_id: DeviceId },
/// Touchpad rotation event with two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **macOS**.
TouchpadRotate {
device_id: DeviceId,
delta: f32,
phase: TouchPhase,
},
/// Touchpad pressure event.
@ -136,89 +79,62 @@ pub enum InputEvent {
/// Touch event has been received
///
/// ## Platform-specific
///
/// - **Web**: Doesnt take into account CSS border, padding, or transform.
/// - **macOS:** Unsupported.
Touch(Touch),
}
#[derive(Debug, PartialEq)]
pub enum InputEventConversionError<'a> {
FromError(&'a WindowEvent<'a>)
}
impl<'a> TryFrom<&'a WindowEvent<'a>> for InputEvent {
type Error = InputEventConversionError<'a>;
fn try_from(value: &'a WindowEvent<'a>) -> Result<Self, Self::Error> {
impl InputEvent {
pub fn from_window_event(value: &WindowEvent) -> Option<Self> {
match value {
WindowEvent::KeyboardInput { device_id, input, is_synthetic } =>
Ok(InputEvent::KeyboardInput {
WindowEvent::KeyboardInput { device_id, event, is_synthetic } =>
Some(InputEvent::KeyboardInput {
device_id: *device_id,
input: *input,
event: event.clone(),
is_synthetic: *is_synthetic
}),
#[allow(deprecated, reason="Compatibility")]
WindowEvent::CursorMoved { device_id, position, modifiers } =>
Ok(InputEvent::CursorMoved {
WindowEvent::CursorMoved { device_id, position, } =>
Some(InputEvent::CursorMoved {
device_id: *device_id,
position: *position,
modifiers: *modifiers
}),
WindowEvent::CursorEntered { device_id } =>
Ok(InputEvent::CursorEntered {
Some(InputEvent::CursorEntered {
device_id: *device_id
}),
WindowEvent::CursorLeft { device_id } =>
Ok(InputEvent::CursorLeft {
Some(InputEvent::CursorLeft {
device_id: *device_id
}),
#[allow(deprecated, reason="Compatibility")]
WindowEvent::MouseWheel { device_id, delta, phase, modifiers } =>
Ok(InputEvent::MouseWheel {
WindowEvent::MouseWheel { device_id, delta, phase } =>
Some(InputEvent::MouseWheel {
device_id: *device_id,
delta: *delta,
phase: *phase,
modifiers: *modifiers
}),
#[allow(deprecated, reason="Compatibility")]
WindowEvent::MouseInput { device_id, state, button, modifiers } =>
Ok(InputEvent::MouseInput {
WindowEvent::MouseInput { device_id, state, button } =>
Some(InputEvent::MouseInput {
device_id: *device_id,
state: *state,
button: *button,
modifiers: *modifiers
}),
WindowEvent::TouchpadMagnify { device_id, delta, phase } =>
Ok(InputEvent::TouchpadMagnify {
device_id: *device_id,
delta: *delta,
phase: *phase
}),
WindowEvent::SmartMagnify { device_id } =>
Ok(InputEvent::SmartMagnify {
device_id: *device_id
}),
WindowEvent::TouchpadRotate { device_id, delta, phase } =>
Ok(InputEvent::TouchpadRotate {
device_id: *device_id,
delta: *delta,
phase: *phase
}),
WindowEvent::TouchpadPressure { device_id, pressure, stage } =>
Ok(InputEvent::TouchpadPressure {
Some(InputEvent::TouchpadPressure {
device_id: *device_id,
pressure: *pressure,
stage: *stage
}),
WindowEvent::AxisMotion { device_id, axis, value } =>
Ok(InputEvent::AxisMotion {
Some(InputEvent::AxisMotion {
device_id: *device_id,
axis: *axis,
value: *value
}),
WindowEvent::Touch(t) => Ok(InputEvent::Touch(*t)),
_ => Err(InputEventConversionError::FromError(value))
WindowEvent::Touch(t) => Some(InputEvent::Touch(*t)),
_ => None,
}
}
}

View File

@ -13,7 +13,7 @@ pub use buttons::*;
pub mod action;
pub use action::*;
pub type KeyCode = winit::event::VirtualKeyCode;
pub type KeyCode = winit::keyboard::KeyCode;
/// Parses a [`KeyCode`] from a [`&str`].
///
@ -24,42 +24,42 @@ pub fn keycode_from_str(s: &str) -> Option<KeyCode> {
let s = s.as_str();
match s {
"1" => Some(KeyCode::Key1),
"2" => Some(KeyCode::Key2),
"3" => Some(KeyCode::Key3),
"4" => Some(KeyCode::Key4),
"5" => Some(KeyCode::Key5),
"6" => Some(KeyCode::Key6),
"7" => Some(KeyCode::Key7),
"8" => Some(KeyCode::Key8),
"9" => Some(KeyCode::Key9),
"0" => Some(KeyCode::Key0),
"a" => Some(KeyCode::A),
"b" => Some(KeyCode::B),
"c" => Some(KeyCode::C),
"d" => Some(KeyCode::D),
"e" => Some(KeyCode::E),
"f" => Some(KeyCode::F),
"g" => Some(KeyCode::G),
"h" => Some(KeyCode::H),
"i" => Some(KeyCode::I),
"j" => Some(KeyCode::J),
"k" => Some(KeyCode::K),
"l" => Some(KeyCode::L),
"m" => Some(KeyCode::M),
"n" => Some(KeyCode::N),
"o" => Some(KeyCode::O),
"p" => Some(KeyCode::P),
"q" => Some(KeyCode::Q),
"r" => Some(KeyCode::R),
"s" => Some(KeyCode::S),
"t" => Some(KeyCode::T),
"u" => Some(KeyCode::U),
"v" => Some(KeyCode::V),
"w" => Some(KeyCode::W),
"x" => Some(KeyCode::X),
"y" => Some(KeyCode::Y),
"z" => Some(KeyCode::Z),
"1" => Some(KeyCode::Digit1),
"2" => Some(KeyCode::Digit2),
"3" => Some(KeyCode::Digit3),
"4" => Some(KeyCode::Digit4),
"5" => Some(KeyCode::Digit5),
"6" => Some(KeyCode::Digit6),
"7" => Some(KeyCode::Digit7),
"8" => Some(KeyCode::Digit8),
"9" => Some(KeyCode::Digit9),
"0" => Some(KeyCode::Digit0),
"a" => Some(KeyCode::KeyA),
"b" => Some(KeyCode::KeyB),
"c" => Some(KeyCode::KeyC),
"d" => Some(KeyCode::KeyD),
"e" => Some(KeyCode::KeyE),
"f" => Some(KeyCode::KeyF),
"g" => Some(KeyCode::KeyG),
"h" => Some(KeyCode::KeyH),
"i" => Some(KeyCode::KeyI),
"j" => Some(KeyCode::KeyJ),
"k" => Some(KeyCode::KeyK),
"l" => Some(KeyCode::KeyL),
"m" => Some(KeyCode::KeyM),
"n" => Some(KeyCode::KeyN),
"o" => Some(KeyCode::KeyO),
"p" => Some(KeyCode::KeyP),
"q" => Some(KeyCode::KeyQ),
"r" => Some(KeyCode::KeyR),
"s" => Some(KeyCode::KeyS),
"t" => Some(KeyCode::KeyT),
"u" => Some(KeyCode::KeyU),
"v" => Some(KeyCode::KeyV),
"w" => Some(KeyCode::KeyW),
"x" => Some(KeyCode::KeyX),
"y" => Some(KeyCode::KeyY),
"z" => Some(KeyCode::KeyZ),
"escape" => Some(KeyCode::Escape),
"f1" => Some(KeyCode::F1),
"f2" => Some(KeyCode::F2),
@ -85,25 +85,23 @@ pub fn keycode_from_str(s: &str) -> Option<KeyCode> {
"f22" => Some(KeyCode::F22),
"f23" => Some(KeyCode::F23),
"f24" => Some(KeyCode::F24),
"snapshot" => Some(KeyCode::Snapshot),
"scroll" => Some(KeyCode::Scroll),
"print_screen" => Some(KeyCode::PrintScreen),
"scroll_lock" => Some(KeyCode::ScrollLock),
"pause" => Some(KeyCode::Pause),
"insert" => Some(KeyCode::Insert),
"home" => Some(KeyCode::Home),
"delete" => Some(KeyCode::Delete),
"end" => Some(KeyCode::End),
"pagedown" => Some(KeyCode::PageDown),
"pageup" => Some(KeyCode::PageUp),
"left" => Some(KeyCode::Left),
"up" => Some(KeyCode::Up),
"right" => Some(KeyCode::Right),
"down" => Some(KeyCode::Down),
"back" => Some(KeyCode::Back),
"return" => Some(KeyCode::Return),
"page_down" => Some(KeyCode::PageDown),
"page_up" => Some(KeyCode::PageUp),
"left" => Some(KeyCode::ArrowLeft),
"up" => Some(KeyCode::ArrowUp),
"right" => Some(KeyCode::ArrowRight),
"down" => Some(KeyCode::ArrowDown),
"backspace" => Some(KeyCode::Backspace),
"enter" => Some(KeyCode::Enter),
"space" => Some(KeyCode::Space),
"compose" => Some(KeyCode::Compose),
"caret" => Some(KeyCode::Caret),
"numlock" => Some(KeyCode::Numlock),
"numlock" => Some(KeyCode::NumLock),
"numpad0" => Some(KeyCode::Numpad0),
"numpad1" => Some(KeyCode::Numpad1),
"numpad2" => Some(KeyCode::Numpad2),
@ -114,76 +112,62 @@ pub fn keycode_from_str(s: &str) -> Option<KeyCode> {
"numpad7" => Some(KeyCode::Numpad7),
"numpad8" => Some(KeyCode::Numpad8),
"numpad9" => Some(KeyCode::Numpad9),
"numpadadd" => Some(KeyCode::NumpadAdd),
"numpaddivide" => Some(KeyCode::NumpadDivide),
"numpaddecimal" => Some(KeyCode::NumpadDecimal),
"numpadcomma" => Some(KeyCode::NumpadComma),
"numpadenter" => Some(KeyCode::NumpadEnter),
"numpadequals" => Some(KeyCode::NumpadEquals),
"numpadmultiply" => Some(KeyCode::NumpadMultiply),
"numpadsubtract" => Some(KeyCode::NumpadSubtract),
"abntc1" => Some(KeyCode::AbntC1),
"abntc2" => Some(KeyCode::AbntC2),
"apostrophe" => Some(KeyCode::Apostrophe),
"apps" => Some(KeyCode::Apps),
"asterisk" => Some(KeyCode::Asterisk),
"at" => Some(KeyCode::At),
"ax" => Some(KeyCode::Ax),
"numpad_add" => Some(KeyCode::NumpadAdd),
"numpad_divide" => Some(KeyCode::NumpadDivide),
"numpad_decimal" => Some(KeyCode::NumpadDecimal),
"numpad_comma" => Some(KeyCode::NumpadComma),
"numpad_enter" => Some(KeyCode::NumpadEnter),
"numpad_multiply" => Some(KeyCode::NumpadMultiply),
"numpad_subtract" => Some(KeyCode::NumpadSubtract),
"numpad_star" => Some(KeyCode::NumpadStar),
"quote" => Some(KeyCode::Quote),
"launch_app1" => Some(KeyCode::LaunchApp1),
"launch_app1" => Some(KeyCode::LaunchApp2),
"backslash" => Some(KeyCode::Backslash),
"calculator" => Some(KeyCode::Calculator),
"capital" => Some(KeyCode::Capital),
"colon" => Some(KeyCode::Colon),
"caps_lock" => Some(KeyCode::CapsLock),
"comma" => Some(KeyCode::Comma),
"convert" => Some(KeyCode::Convert),
"equals" => Some(KeyCode::Equals),
"grave" => Some(KeyCode::Grave),
"kana" => Some(KeyCode::Kana),
"kanji" => Some(KeyCode::Kanji),
"lalt" => Some(KeyCode::LAlt),
"lbracket" => Some(KeyCode::LBracket),
"lcontrol" => Some(KeyCode::LControl),
"lshift" => Some(KeyCode::LShift),
"lwin" => Some(KeyCode::LWin),
"mail" => Some(KeyCode::Mail),
"mediaselect" => Some(KeyCode::MediaSelect),
"mediastop" => Some(KeyCode::MediaStop),
"equal" => Some(KeyCode::Equal),
"grave" | "backquote" => Some(KeyCode::Backquote),
"kana_mode" => Some(KeyCode::KanaMode),
"katakana" => Some(KeyCode::Katakana),
"alt_left" => Some(KeyCode::AltLeft),
"alt_right" => Some(KeyCode::AltRight),
"bracket_left" => Some(KeyCode::BracketLeft),
"bracket_right" => Some(KeyCode::BracketRight),
"control_left" => Some(KeyCode::ControlLeft),
"control-right" => Some(KeyCode::ControlRight),
"shift_left" => Some(KeyCode::ShiftLeft),
"shift_right" => Some(KeyCode::ShiftRight),
"meta" => Some(KeyCode::Meta),
"mail" => Some(KeyCode::LaunchMail),
"media_select" => Some(KeyCode::MediaSelect),
"media_stop" => Some(KeyCode::MediaStop),
"stop" => Some(KeyCode::MediaStop),
"track_next" => Some(KeyCode::MediaTrackNext),
"track_prev" => Some(KeyCode::MediaTrackPrevious),
"minus" => Some(KeyCode::Minus),
"mute" => Some(KeyCode::Mute),
"mycomputer" => Some(KeyCode::MyComputer),
"navigateforward" => Some(KeyCode::NavigateForward),
"navigatebackward" => Some(KeyCode::NavigateBackward),
"nexttrack" => Some(KeyCode::NextTrack),
"noconvert" => Some(KeyCode::NoConvert),
"oem102" => Some(KeyCode::OEM102),
"mute" => Some(KeyCode::AudioVolumeMute),
"browser_forward" => Some(KeyCode::BrowserForward),
"browser_back" => Some(KeyCode::BrowserBack),
"webfavorites" => Some(KeyCode::BrowserFavorites),
"webhome" => Some(KeyCode::BrowserHome),
"webrefresh" => Some(KeyCode::BrowserRefresh),
"websearch" => Some(KeyCode::BrowserSearch),
"webstop" => Some(KeyCode::BrowserStop),
"non_convert" => Some(KeyCode::NonConvert),
"period" => Some(KeyCode::Period),
"playpause" => Some(KeyCode::PlayPause),
"plus" => Some(KeyCode::Plus),
"play_pause" => Some(KeyCode::MediaPlayPause),
"plus" => Some(KeyCode::NumpadAdd),
"power" => Some(KeyCode::Power),
"prevtrack" => Some(KeyCode::PrevTrack),
"ralt" => Some(KeyCode::RAlt),
"rbracket" => Some(KeyCode::RBracket),
"rcontrol" => Some(KeyCode::RControl),
"rshift" => Some(KeyCode::RShift),
"rwin" => Some(KeyCode::RWin),
"semicolon" => Some(KeyCode::Semicolon),
"slash" => Some(KeyCode::Slash),
"sleep" => Some(KeyCode::Sleep),
"stop" => Some(KeyCode::Stop),
"sysrq" => Some(KeyCode::Sysrq),
"tab" => Some(KeyCode::Tab),
"underline" => Some(KeyCode::Underline),
"unlabeled" => Some(KeyCode::Unlabeled),
"volumedown" => Some(KeyCode::VolumeDown),
"volumeup" => Some(KeyCode::VolumeUp),
"wake" => Some(KeyCode::Wake),
"webback" => Some(KeyCode::WebBack),
"webfavorites" => Some(KeyCode::WebFavorites),
"webforward" => Some(KeyCode::WebForward),
"webhome" => Some(KeyCode::WebHome),
"webrefresh" => Some(KeyCode::WebRefresh),
"websearch" => Some(KeyCode::WebSearch),
"webstop" => Some(KeyCode::WebStop),
"yen" => Some(KeyCode::Yen),
"volume_down" => Some(KeyCode::AudioVolumeDown),
"volume_up" => Some(KeyCode::AudioVolumeUp),
"wake_up" => Some(KeyCode::WakeUp),
"yen" => Some(KeyCode::IntlYen),
"copy" => Some(KeyCode::Copy),
"paste" => Some(KeyCode::Paste),
"cut" => Some(KeyCode::Cut),

View File

@ -2,7 +2,7 @@ use std::ptr::NonNull;
use glam::Vec2;
use lyra_ecs::{World, system::IntoSystem};
use winit::event::MouseScrollDelta;
use winit::{event::MouseScrollDelta, keyboard::PhysicalKey};
use crate::{EventQueue, plugin::Plugin, game::GameStages};
@ -20,21 +20,14 @@ impl InputSystem {
let mut event_queue = event_queue.unwrap();
match event {
InputEvent::KeyboardInput { input, .. } => {
if let Some(code) = input.virtual_keycode {
InputEvent::KeyboardInput { device_id, event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
drop(event_queue);
let mut e = world.get_resource_or_else(InputButtons::<winit::event::VirtualKeyCode>::new);
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
e.add_input_from_winit(code, input.state);
e.add_input_from_winit(code, event.state);
}
},
InputEvent::MouseMotion { delta, .. } => {
let delta = MouseMotion {
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
};
event_queue.trigger_event(delta);
},
InputEvent::CursorMoved { position, .. } => {
let exact = MouseExact {
pos: Vec2::new(position.x as f32, position.y as f32)
@ -67,6 +60,8 @@ impl InputSystem {
winit::event::MouseButton::Left => MouseButton::Left,
winit::event::MouseButton::Right => MouseButton::Right,
winit::event::MouseButton::Middle => MouseButton::Middle,
winit::event::MouseButton::Back => MouseButton::Back,
winit::event::MouseButton::Forward => MouseButton::Forward,
winit::event::MouseButton::Other(v) => MouseButton::Other(*v),
};
@ -102,7 +97,7 @@ impl crate::ecs::system::System for InputSystem {
let queue = world.try_get_resource_mut::<EventQueue>()
.and_then(|q| q.read_events::<InputEvent>());
let mut e = world.get_resource_or_else(InputButtons::<winit::event::VirtualKeyCode>::new);
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
e.update();
drop(e);
@ -140,7 +135,7 @@ impl IntoSystem<()> for InputSystem {
pub struct InputPlugin;
impl Plugin for InputPlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.add_system_to_stage(GameStages::PreUpdate, "input", InputSystem, &[]);
fn setup(&self, app: &mut crate::game::App) {
app.add_system_to_stage(GameStages::PreUpdate, "input", InputSystem, &[]);
}
}

View File

@ -1,35 +1,53 @@
use std::cell::Cell;
use std::cell::OnceCell;
use std::cell::RefCell;
use std::ptr::NonNull;
use lyra_ecs::system::System;
use lyra_ecs::CommandQueue;
use lyra_resource::ResourceManager;
use crate::game::App;
use crate::EventsPlugin;
use crate::DeltaTimePlugin;
use crate::game::Game;
use crate::input::InputPlugin;
use crate::render::window::WindowPlugin;
/// A Plugin is something you can add to a `Game` that can be used to define systems, or spawn initial entities.
pub trait Plugin {
/// Setup this plugin. This runs before the game has started
fn setup(&self, game: &mut Game);
/// Setup this plugin. This runs before the app has started
fn setup(&self, app: &mut App);
fn is_ready(&self, _game: &mut Game) -> bool {
fn is_ready(&self, app: &mut App) -> bool {
true
}
fn complete(&self, _game: &mut Game) {
fn complete(&self, _app: &mut App) {
}
fn cleanup(&self, _game: &mut Game) {
fn cleanup(&self, _app: &mut App) {
}
}
impl<P> Plugin for P
where P: Fn(&mut Game)
where P: Fn(&mut App)
{
fn setup(&self, game: &mut Game) {
self(game);
fn setup(&self, app: &mut App) {
self(app);
}
}
/// An ECS system converted to a plugin.
///
/// The system is executed in plugin setup phase.
pub(crate) struct SystemPlugin<S: System>(pub RefCell<S>);
impl<S: System> Plugin for SystemPlugin<S> {
fn setup(&self, app: &mut App) {
let mut s = self.0.borrow_mut();
s.execute(NonNull::from(&app.world));
}
}
@ -56,9 +74,9 @@ impl PluginSet {
}
impl Plugin for PluginSet {
fn setup(&self, game: &mut Game) {
fn setup(&self, app: &mut App) {
for plugin in self.plugins.iter() {
plugin.setup(game);
plugin.setup(app);
}
}
}
@ -98,8 +116,8 @@ impl_tuple_plugin_set! { (C0, 0) (C1, 1) (C2, 2) (C3, 3) (C4, 4) (C5, 5) (C6, 6)
pub struct ResourceManagerPlugin;
impl Plugin for ResourceManagerPlugin {
fn setup(&self, game: &mut Game) {
game.world_mut().add_resource(ResourceManager::new());
fn setup(&self, app: &mut App) {
app.world.add_resource(ResourceManager::new());
}
}
@ -108,13 +126,13 @@ impl Plugin for ResourceManagerPlugin {
pub struct DefaultPlugins;
impl Plugin for DefaultPlugins {
fn setup(&self, game: &mut Game) {
CommandQueuePlugin.setup(game);
EventsPlugin.setup(game);
InputPlugin.setup(game);
ResourceManagerPlugin.setup(game);
WindowPlugin::default().setup(game);
DeltaTimePlugin.setup(game);
fn setup(&self, app: &mut App) {
CommandQueuePlugin.setup(app);
EventsPlugin.setup(app);
InputPlugin.setup(app);
ResourceManagerPlugin.setup(app);
WindowPlugin::default().setup(app);
DeltaTimePlugin.setup(app);
}
}
@ -124,7 +142,7 @@ impl Plugin for DefaultPlugins {
pub struct CommandQueuePlugin;
impl Plugin for CommandQueuePlugin {
fn setup(&self, game: &mut Game) {
game.world_mut().add_resource(CommandQueue::default());
fn setup(&self, app: &mut App) {
app.world.add_resource(CommandQueue::default());
}
}

View File

@ -22,7 +22,7 @@ pub struct RenderGraphContext<'a> {
pub device: Arc<wgpu::Device>,
pub queue: Arc<wgpu::Queue>,
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a>>,
/// The label of this Node.
pub label: RenderGraphLabelValue,
}
@ -41,7 +41,7 @@ impl<'a> RenderGraphContext<'a> {
pub fn begin_render_pass(
&'a mut self,
desc: wgpu::RenderPassDescriptor<'a, 'a>,
desc: wgpu::RenderPassDescriptor<'a>,
) -> wgpu::RenderPass {
self.encoder
.as_mut()

View File

@ -17,7 +17,7 @@ pub struct FxaaPass {
/// Store bind groups for the input textures.
/// The texture may change due to resizes, or changes to the view target chain
/// from other nodes.
bg_cache: HashMap<wgpu::Id, wgpu::BindGroup>,
bg_cache: HashMap<wgpu::Id<wgpu::TextureView>, wgpu::BindGroup>,
}
impl FxaaPass {
@ -157,10 +157,12 @@ impl Node for FxaaPass {
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None, // TODO: occlusion queries
});
pass.set_pipeline(pipeline.as_render());

View File

@ -235,6 +235,8 @@ impl Node for LightCullComputePass {
],
shader,
shader_entry_point: "cs_main".into(),
cache: None,
compilation_options: Default::default(),
});
self.pipeline = Some(pipeline);
@ -251,6 +253,7 @@ impl Node for LightCullComputePass {
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: Some("light_cull_pass"),
..Default::default()
});
pass.set_pipeline(pipeline);

View File

@ -604,8 +604,8 @@ impl GpuMaterial {
&img.to_rgba8(),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dim.0),
rows_per_image: std::num::NonZeroU32::new(dim.1),
bytes_per_row: Some(4 * dim.0),
rows_per_image: Some(dim.1),
},
wgpu::Extent3d {
width: dim.0,

View File

@ -420,7 +420,7 @@ impl Node for MeshPass {
b: 0.3,
a: 1.0,
}),
store: true,
store: wgpu::StoreOp::Store,
},
})],
// enable depth buffer
@ -428,10 +428,11 @@ impl Node for MeshPass {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
pass.set_pipeline(pipeline);

View File

@ -865,10 +865,11 @@ impl Node for ShadowMapsPass {
view: atlas.view(),
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
for light_depth_map in self.depth_maps.values() {

View File

@ -17,7 +17,7 @@ pub struct TintPass {
/// Store bind groups for the input textures.
/// The texture may change due to resizes, or changes to the view target chain
/// from other nodes.
bg_cache: HashMap<wgpu::Id, wgpu::BindGroup>,
bg_cache: HashMap<wgpu::Id<wgpu::TextureView>, wgpu::BindGroup>,
}
impl TintPass {
@ -152,10 +152,12 @@ impl Node for TintPass {
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(pipeline.as_render());

View File

@ -6,10 +6,19 @@ use crate::math;
enum RenderTargetInner {
Surface {
surface: wgpu::Surface,
/// The surface that will be rendered to.
///
/// You can create a new surface with a `'static` lifetime if you have an `Arc<Window>`:
/// ```nobuild
/// let window = Arc::new(window);
/// let surface = instance.create_surface(Arc::clone(&window))?;
/// ```
surface: wgpu::Surface<'static>,
/// the configuration of the surface render target..
config: wgpu::SurfaceConfiguration,
},
Texture {
/// The texture that will be rendered to.
texture: Arc<wgpu::Texture>,
}
}
@ -25,7 +34,7 @@ impl From<wgpu::Texture> for RenderTarget {
}
impl RenderTarget {
pub fn from_surface(surface: wgpu::Surface, config: wgpu::SurfaceConfiguration) -> Self {
pub fn from_surface(surface: wgpu::Surface<'static>, config: wgpu::SurfaceConfiguration) -> Self {
Self(RenderTargetInner::Surface { surface, config })
}

View File

@ -1,4 +1,4 @@
use std::{mem, num::{NonZeroU32, NonZeroU8}};
use std::mem;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TextureViewDescriptor {
@ -16,13 +16,13 @@ pub struct TextureViewDescriptor {
/// Mip level count.
/// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
/// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
pub mip_level_count: Option<NonZeroU32>,
pub mip_level_count: Option<u32>,
/// Base array layer.
pub base_array_layer: u32,
/// Layer count.
/// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
/// If `None`, considered to include the rest of the array layers, but at least 1 in total.
pub array_layer_count: Option<NonZeroU32>,
pub array_layer_count: Option<u32>,
}
impl TextureViewDescriptor {
@ -79,7 +79,7 @@ pub struct SamplerDescriptor {
/// If this is enabled, this is a comparison sampler using the given comparison function.
pub compare: Option<wgpu::CompareFunction>,
/// Valid values: 1, 2, 4, 8, and 16.
pub anisotropy_clamp: Option<NonZeroU8>,
pub anisotropy_clamp: u16,
/// Border color to use when address_mode is [`AddressMode::ClampToBorder`]
pub border_color: Option<wgpu::SamplerBorderColor>,
}

View File

@ -75,10 +75,11 @@ impl BasicRenderer {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
dx12_shader_compiler: Default::default(),
flags: wgpu::InstanceFlags::default(),
gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
});
// This should be safe since surface will live as long as the window that created it
let surface = unsafe { instance.create_surface(window.as_ref()) }.unwrap();
let surface: wgpu::Surface::<'static> = instance.create_surface(window.clone()).unwrap();
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
@ -90,11 +91,12 @@ impl BasicRenderer {
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
label: None,
required_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
// WebGL does not support all wgpu features.
// Not sure if the engine will ever completely support WASM,
// but its here just in case
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits {
@ -102,7 +104,7 @@ impl BasicRenderer {
..Default::default()
}
},
label: None,
memory_hints: wgpu::MemoryHints::MemoryUsage,
},
None,
).await.unwrap();
@ -113,7 +115,7 @@ impl BasicRenderer {
let surface_format = surface_caps.formats.iter()
.copied()
.find(|f| f.describe().srgb)
.find(|f| f.is_srgb())
.unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
@ -122,6 +124,7 @@ impl BasicRenderer {
height: size.height,
present_mode: wgpu::PresentMode::default(), //wgpu::PresentMode::Mailbox, // "Fast Vsync"
alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2,
view_formats: vec![],
};
surface.configure(&device, &config);

View File

@ -2,19 +2,25 @@ use std::{ops::Deref, rc::Rc, sync::Arc};
use wgpu::PipelineLayout;
use super::Shader;
use super::{PipelineCompilationOptions, Shader};
//#[derive(Debug, Clone)]
pub struct ComputePipelineDescriptor {
pub label: Option<String>,
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
// TODO: make this a ResHandle<Shader>
/// The compiled shader module for the stage.
pub shader: Rc<Shader>,
/// The entry point in the compiled shader.
/// There must be a function in the shader with the same name.
pub shader_entry_point: String,
/// Advanced options for when this pipeline is compiled
///
/// This implements `Default`, and for most users can be set to `Default::default()`
pub compilation_options: PipelineCompilationOptions,
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
/// The pipeline cache to use when creating this pipeline.
pub cache: Option<Arc<wgpu::PipelineCache>>,
}
impl ComputePipelineDescriptor {
@ -28,7 +34,7 @@ impl ComputePipelineDescriptor {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
bind_group_layouts: &bgs,
bind_group_layouts: &bgs,
push_constant_ranges: &self.push_constant_ranges,
})
}
@ -85,6 +91,8 @@ impl ComputePipeline {
layout: layout.as_ref(),
module: &compiled_shader,
entry_point: &desc.shader_entry_point,
cache: desc.cache.as_ref().map(|c| &**c),
compilation_options: desc.compilation_options.as_wgpu(),
};
let pipeline = device.create_compute_pipeline(&desc);

View File

@ -1,4 +1,6 @@
mod shader;
use std::collections::HashMap;
pub use shader::*;
mod pipeline;
@ -11,4 +13,21 @@ mod render_pipeline;
pub use render_pipeline::*;
mod pass;
pub use pass::*;
pub use pass::*;
#[derive(Default, Clone)]
pub struct PipelineCompilationOptions {
pub constants: HashMap<String, f64>,
pub zero_initialize_workgroup_memory: bool,
pub vertex_pulling_transform: bool,
}
impl PipelineCompilationOptions {
pub fn as_wgpu(&self) -> wgpu::PipelineCompilationOptions {
wgpu::PipelineCompilationOptions {
constants: &self.constants,
zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory,
vertex_pulling_transform: self.vertex_pulling_transform,
}
}
}

View File

@ -97,6 +97,7 @@ impl RenderPipeline {
module: &vrtx_shad,
entry_point: &desc.vertex.entry_point,
buffers: &vrtx_buffs,
compilation_options: Default::default(),
};
let frag_module = desc.fragment.as_ref().map(|f| {
@ -115,6 +116,7 @@ impl RenderPipeline {
module: fm.unwrap(),
entry_point: &f.entry_point,
targets: &f.targets,
compilation_options: Default::default(),
});
let render_desc = wgpu::RenderPipelineDescriptor {
@ -126,6 +128,7 @@ impl RenderPipeline {
multisample: desc.multisample,
fragment: fstate,
multiview: desc.multiview,
cache: None,
};
let render_pipeline = device.create_render_pipeline(&render_desc);

View File

@ -105,8 +105,8 @@ impl RenderTexture {
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
bytes_per_row: Some(4 * dimensions.0),
rows_per_image: Some(dimensions.1),
},
size,
);
@ -169,8 +169,8 @@ impl RenderTexture {
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
bytes_per_row: Some(4 * dimensions.0),
rows_per_image: Some(dimensions.1),
},
size,
);
@ -247,8 +247,8 @@ impl RenderTexture {
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
bytes_per_row: Some(4 * dimensions.0),
rows_per_image: Some(dimensions.1),
},
size,
);

View File

@ -1,13 +1,17 @@
use std::sync::Arc;
use glam::{Vec2, IVec2};
use glam::{IVec2, Vec2};
use lyra_ecs::World;
use tracing::{warn, error};
use winit::{window::{Window, Fullscreen}, dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, error::ExternalError};
use tracing::{error, warn};
use winit::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition},
error::ExternalError,
window::{Fullscreen, Window},
};
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
use crate::{plugin::Plugin, change_tracker::Ct, input::InputEvent, EventQueue};
use crate::{change_tracker::Ct, input::InputEvent, plugin::Plugin, EventQueue};
#[derive(Default, Clone)]
pub enum WindowMode {
@ -20,6 +24,75 @@ pub enum WindowMode {
Windowed,
}
#[derive(Default, Clone, Copy)]
pub struct Area {
position: Position,
size: Size,
}
#[derive(Clone, Copy)]
pub enum Size {
Physical { x: i32, y: i32 },
Logical { x: f64, y: f64 },
}
impl Default for Size {
fn default() -> Self {
Self::Physical { x: 0, y: 0 }
}
}
impl Into<winit::dpi::Size> for Size {
fn into(self) -> winit::dpi::Size {
match self {
Size::Physical { x, y } => winit::dpi::PhysicalSize::new(x, y).into(),
Size::Logical { x, y } => winit::dpi::LogicalSize::new(x, y).into(),
}
}
}
impl Size {
pub fn new_physical(x: i32, y: i32) -> Self {
Self::Physical { x, y }
}
pub fn new_logical(x: f64, y: f64) -> Self {
Self::Logical { x, y }
}
}
#[derive(Clone, Copy)]
pub enum Position {
Physical { x: i32, y: i32 },
Logical { x: f64, y: f64 },
}
impl Default for Position {
fn default() -> Self {
Self::Physical { x: 0, y: 0 }
}
}
impl Into<winit::dpi::Position> for Position {
fn into(self) -> winit::dpi::Position {
match self {
Position::Physical { x, y } => winit::dpi::PhysicalPosition::new(x, y).into(),
Position::Logical { x, y } => winit::dpi::LogicalPosition::new(x, y).into(),
}
}
}
impl Position {
pub fn new_physical(x: i32, y: i32) -> Self {
Self::Physical { x, y }
}
pub fn new_logical(x: f64, y: f64) -> Self {
Self::Logical { x, y }
}
}
/// Options that the window will be created with.
#[derive(Clone)]
pub struct WindowOptions {
@ -43,11 +116,13 @@ pub struct WindowOptions {
/// * iOS / Android / Web / X11 / Orbital: Unsupported.
pub cursor_hittest: bool,
/// The cursor icon of the window.
/// Modifies the cursor icon of the window.
///
/// Platform-specific
/// Platform-specific:
/// * iOS / Android / Orbital: Unsupported.
pub cursor_icon: CursorIcon,
/// * Web: Custom cursors have to be loaded and decoded first, until then the previous
/// cursor is shown.
pub cursor: winit::window::Cursor,
/// The cursors visibility.
/// If false, this will hide the cursor. If true, this will show the cursor.
@ -87,30 +162,42 @@ pub struct WindowOptions {
/// * iOS / Android / Web / Orbital: Unsupported.
pub ime_allowed: bool,
/// Sets location of IME candidate box in client area coordinates relative to the top left.
/// Set the IME cursor editing area, where the `position` is the top left corner of that
/// area and `size` is the size of this area starting from the position. An example of such
/// area could be a input field in the UI or line in the editor.
///
/// This is the window / popup / overlay that allows you to select the desired characters.
/// The look of this box may differ between input devices, even on the same platform.
pub ime_position: Vec2,
/// The windowing system could place a candidate box close to that area, but try to not
/// obscure the specified area, so the user input to it stays visible.
///
/// The candidate box is the window / popup / overlay that allows you to select the desired
/// characters. The look of this box may differ between input devices, even on the same
/// platform.
///
/// (Apples official term is “candidate window”, see their chinese and japanese guides).
///
/// Platform-specific
/// * **X11:** - area is not supported, only position.
/// * **iOS / Android / Web / Orbital:** Unsupported.
pub ime_cursor_area: Area,
/// Modifies the inner size of the window.
///
/// Platform-specific:
/// * iOS / Android: Unsupported.
/// * Web: Sets the size of the canvas element.
pub inner_size: IVec2,
pub inner_size: Size,
/// Sets a maximum dimension size for the window.
///
/// Platform-specific:
/// * iOS / Android / Web / Orbital: Unsupported.
pub max_inner_size: Option<IVec2>,
pub max_inner_size: Option<Size>,
/// Sets a minimum dimension size for the window.
///
/// Platform-specific:
/// * iOS / Android / Web / Orbital: Unsupported.
pub min_inner_size: Option<IVec2>,
pub min_inner_size: Option<Size>,
/// Sets the window to maximized or back.
///
@ -186,14 +273,14 @@ impl Default for WindowOptions {
content_protected: false,
cursor_grab: CursorGrabMode::None,
cursor_hittest: true,
cursor_icon: CursorIcon::Default,
cursor: Default::default(),
cursor_visible: true,
decorations: true,
enabled_buttons: WindowButtons::all(),
mode: WindowMode::Windowed,
ime_allowed: false,
ime_position: Default::default(),
inner_size: glam::i32::IVec2::new(800, 600),
ime_cursor_area: Area::default(),
inner_size: Size::new_physical(800, 600),
max_inner_size: None,
min_inner_size: None,
maximized: false,
@ -224,7 +311,10 @@ fn vec2_to_logical_pos(pos: Vec2) -> LogicalPosition<f32> {
/// Convert an IVec2 to a LogicalSize<i32>
fn ivec2_to_logical_size(size: IVec2) -> LogicalSize<i32> {
LogicalSize { width: size.x, height: size.y }
LogicalSize {
width: size.x,
height: size.y,
}
}
/// Convert an Option<IVec2> to an Option<LogicalSize<i32>>
@ -234,7 +324,10 @@ fn ivec2_to_logical_size_op(size: Option<IVec2>) -> Option<LogicalSize<i32>> {
/// Convert an Option<Vec2> to an Option<LogicalSize<f32>>
fn vec2_to_logical_size_op(size: Option<Vec2>) -> Option<LogicalSize<f32>> {
size.map(|size| LogicalSize { width: size.x, height: size.y } )
size.map(|size| LogicalSize {
width: size.x,
height: size.y,
})
}
/// Set the cursor grab of a window depending on the platform.
@ -250,9 +343,13 @@ fn set_cursor_grab(window: &Window, grab: &mut CursorGrabMode) -> anyhow::Result
*grab = CursorGrabMode::Confined; // NOTE: May support Locked later
} else if cfg!(target_os = "macos") {
*grab = CursorGrabMode::Locked; // NOTE: May support Confined later
} else if cfg!(any(target_os = "android", target_os = "ios", target_os = "orbital")) {
} else if cfg!(any(
target_os = "android",
target_os = "ios",
target_os = "orbital"
)) {
warn!("CursorGrabMode is not supported on Android, IOS, or Oribital, skipping");
return Ok(())
return Ok(());
}
}
@ -264,8 +361,8 @@ fn set_cursor_grab(window: &Window, grab: &mut CursorGrabMode) -> anyhow::Result
/// if the window is set to confine the cursor, the cursor is invisible,
/// and the window is focused, set the cursor position to the center of the screen.
fn center_mouse(window: &Window, options: &WindowOptions) {
if options.cursor_grab == CursorGrabMode::Confined && !options.cursor_visible
&& options.focused {
if options.cursor_grab == CursorGrabMode::Confined && !options.cursor_visible && options.focused
{
let size = window.inner_size();
let middle = PhysicalPosition {
x: size.width / 2,
@ -276,7 +373,10 @@ fn center_mouse(window: &Window, options: &WindowOptions) {
}
fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
if let (Some(window), Some(opts)) = (world.try_get_resource::<Arc<Window>>(), world.try_get_resource::<Ct<WindowOptions>>()) {
if let (Some(window), Some(opts)) = (
world.try_get_resource::<Arc<Window>>(),
world.try_get_resource::<Ct<WindowOptions>>(),
) {
// if the options changed, update the window
if opts.peek_changed() {
drop(opts); // drop the Ref, we're about to get a RefMut
@ -290,24 +390,30 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
window.set_content_protected(opts.content_protected);
set_cursor_grab(&window, &mut opts.cursor_grab)?;
match window.set_cursor_hittest(opts.cursor_hittest) {
Ok(()) => {},
Err(ExternalError::NotSupported(_)) => { /* ignore */ },
Ok(()) => {}
Err(ExternalError::NotSupported(_)) => { /* ignore */ }
Err(e) => {
error!("OS error when setting cursor hittest: {:?}", e);
}
}
window.set_cursor_icon(opts.cursor_icon); // TODO: Handle unsupported platforms
window.set_cursor(opts.cursor.clone()); // TODO: Handle unsupported platforms
window.set_cursor_visible(opts.cursor_visible); // TODO: Handle unsupported platforms
window.set_decorations(opts.decorations); // TODO: Handle unsupported platforms
window.set_enabled_buttons(opts.enabled_buttons); // TODO: Handle unsupported platforms
// Update the window mode. can only be done if the monitor is found
if let Some(monitor) = window.current_monitor()
.or_else(|| window.primary_monitor())
.or_else(|| window.available_monitors().next()) {
if let Some(monitor) = window
.current_monitor()
.or_else(|| window.primary_monitor())
.or_else(|| window.available_monitors().next())
{
match opts.mode {
WindowMode::Borderless => window.set_fullscreen(Some(Fullscreen::Borderless(Some(monitor)))),
WindowMode::Fullscreen => window.set_fullscreen(Some(Fullscreen::Exclusive(monitor.video_modes().next().unwrap()))),
WindowMode::Borderless => {
window.set_fullscreen(Some(Fullscreen::Borderless(Some(monitor))))
}
WindowMode::Fullscreen => window.set_fullscreen(Some(Fullscreen::Exclusive(
monitor.video_modes().next().unwrap(),
))),
WindowMode::Windowed => window.set_fullscreen(None),
}
} else {
@ -315,13 +421,13 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
}
window.set_ime_allowed(opts.ime_allowed);
window.set_ime_position(vec2_to_logical_pos(opts.ime_position));
window.set_inner_size(ivec2_to_logical_size(opts.inner_size));
window.set_ime_cursor_area(opts.ime_cursor_area.position, opts.ime_cursor_area.size);
window.request_inner_size(opts.inner_size);
if opts.max_inner_size.is_some() {
window.set_max_inner_size(ivec2_to_logical_size_op(opts.max_inner_size));
window.set_max_inner_size(opts.max_inner_size);
}
if opts.min_inner_size.is_some() {
window.set_min_inner_size(ivec2_to_logical_size_op(opts.min_inner_size));
window.set_min_inner_size(opts.min_inner_size);
}
window.set_maximized(opts.maximized);
window.set_minimized(opts.minimized);
@ -346,11 +452,11 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
match ev {
InputEvent::CursorEntered { .. } => {
opts.cursor_inside_window = true;
},
}
InputEvent::CursorLeft { .. } => {
opts.cursor_inside_window = false;
},
_ => {},
}
_ => {}
}
}
}
@ -370,10 +476,10 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
}
impl Plugin for WindowPlugin {
fn setup(&self, game: &mut crate::game::Game) {
fn setup(&self, app: &mut crate::game::App) {
let window_options = WindowOptions::default();
game.world_mut().add_resource(Ct::new(window_options));
game.with_system("window_updater", window_updater_system, &[]);
app.world.add_resource(Ct::new(window_options));
app.with_system("window_updater", window_updater_system, &[]);
}
}

View File

@ -1,7 +1,7 @@
use glam::{EulerRot, Quat, Vec3};
use lyra_ecs::{query::{Res, View}, Component};
use crate::{game::Game, input::ActionHandler, plugin::Plugin, DeltaTime};
use crate::{game::App, input::ActionHandler, plugin::Plugin, DeltaTime};
use super::CameraComponent;
@ -97,7 +97,7 @@ pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<Actio
pub struct FreeFlyCameraPlugin;
impl Plugin for FreeFlyCameraPlugin {
fn setup(&self, game: &mut Game) {
game.with_system("free_fly_camera_system", free_fly_camera_controller, &[]);
fn setup(&self, app: &mut App) {
app.with_system("free_fly_camera_system", free_fly_camera_controller, &[]);
}
}

View File

@ -6,4 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glam = { version = "0.24.0" }
glam = { version = "0.29.0" }

View File

@ -13,9 +13,9 @@ lyra-scene = { path = "../lyra-scene" }
anyhow = "1.0.75"
base64 = "0.21.4"
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
glam = "0.24.1"
glam = "0.29.0"
gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular"] }
image = "0.24.7"
image = "0.25.2"
# not using custom matcher, or file type from file path
infer = { version = "0.15.0", default-features = false }
mime = "0.3.17"

@ -1 +1 @@
Subproject commit 54c9926a04cdef657289fd67730c0b85d1bdda3e
Subproject commit a761f4094bc18190285b4687ec804161fea874b6

View File

@ -1,4 +1,5 @@
[toolchain]
channel = "nightly-2023-11-21"
#channel = "nightly-2023-11-21"
channel = "nightly"
#date = "2023-11-21"
targets = [ "x86_64-unknown-linux-gnu" ]